Merge branch '1567-remove-pending-contact-state-from-db' into 'master'

Remove pending contact state from DB

See merge request briar/briar!1102
This commit is contained in:
Torsten Grote
2019-05-22 16:56:59 +00:00
25 changed files with 280 additions and 172 deletions

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException;
@@ -76,9 +77,11 @@ public interface ContactManager {
throws DbException, FormatException;
/**
* Returns a list of {@link PendingContact}s.
* Returns a list of {@link PendingContact PendingContacts} and their
* {@link PendingContactState states}.
*/
Collection<PendingContact> getPendingContacts() throws DbException;
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
throws DbException;
/**
* Removes a {@link PendingContact}.

View File

@@ -12,15 +12,13 @@ public class PendingContact {
private final PendingContactId id;
private final PublicKey publicKey;
private final String alias;
private final PendingContactState state;
private final long timestamp;
public PendingContact(PendingContactId id, PublicKey publicKey,
String alias, PendingContactState state, long timestamp) {
String alias, long timestamp) {
this.id = id;
this.publicKey = publicKey;
this.alias = alias;
this.state = state;
this.timestamp = timestamp;
}
@@ -36,10 +34,6 @@ public class PendingContact {
return alias;
}
public PendingContactState getState() {
return state;
}
public long getTimestamp() {
return timestamp;
}

View File

@@ -1,30 +1,9 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public enum PendingContactState {
WAITING_FOR_CONNECTION(0),
CONNECTED(1),
ADDING_CONTACT(2),
FAILED(3);
private final int value;
PendingContactState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static PendingContactState fromValue(int value) {
for (PendingContactState s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
WAITING_FOR_CONNECTION,
CONNECTED,
ADDING_CONTACT,
FAILED
}

View File

@@ -0,0 +1,25 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a pending contact is added.
*/
@Immutable
@NotNullByDefault
public class PendingContactAddedEvent extends Event {
private final PendingContact pendingContact;
public PendingContactAddedEvent(PendingContact pendingContact) {
this.pendingContact = pendingContact;
}
public PendingContact getPendingContact() {
return pendingContact;
}
}

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.PrivateKey;
@@ -181,10 +180,7 @@ public class TestUtils {
PendingContactId id = new PendingContactId(getRandomId());
PublicKey publicKey = getAgreementPublicKey();
String alias = getRandomString(nameLength);
int stateIndex =
random.nextInt(PendingContactState.values().length - 1);
PendingContactState state = PendingContactState.values()[stateIndex];
return new PendingContact(id, publicKey, alias, state, timestamp);
return new PendingContact(id, publicKey, alias, timestamp);
}
public static ContactId getContactId() {

View File

@@ -1,11 +1,13 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
@@ -19,6 +21,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -28,6 +31,7 @@ import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
@@ -111,8 +115,16 @@ class ContactManagerImpl implements ContactManager {
}
@Override
public Collection<PendingContact> getPendingContacts() throws DbException {
return db.transactionWithResult(true, db::getPendingContacts);
public Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
throws DbException {
Collection<PendingContact> pendingContacts =
db.transactionWithResult(true, db::getPendingContacts);
List<Pair<PendingContact, PendingContactState>> pairs =
new ArrayList<>(pendingContacts.size());
for (PendingContact p : pendingContacts) {
pairs.add(new Pair<>(p, WAITING_FOR_CONNECTION)); // TODO
}
return pairs;
}
@Override

View File

@@ -20,7 +20,6 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
class PendingContactFactoryImpl implements PendingContactFactory {
@@ -39,8 +38,7 @@ class PendingContactFactoryImpl implements PendingContactFactory {
PublicKey publicKey = parseHandshakeLink(link);
PendingContactId id = getPendingContactId(publicKey);
long timestamp = clock.currentTimeMillis();
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
return new PendingContact(id, publicKey, alias, timestamp);
}
private PublicKey parseHandshakeLink(String link) throws FormatException {

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
@@ -674,12 +673,6 @@ interface Database<T> {
void setMessageState(T txn, MessageId m, MessageState state)
throws DbException;
/**
* Sets the state of the given pending contact.
*/
void setPendingContactState(T txn, PendingContactId p,
PendingContactState state) throws DbException;
/**
* Sets the reordering window for the given transport keys in the given
* time period.

View File

@@ -7,8 +7,8 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
@@ -295,8 +295,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (db.containsPendingContact(txn, p.getId()))
throw new PendingContactExistsException();
db.addPendingContact(txn, p);
transaction.attach(new PendingContactStateChangedEvent(p.getId(),
p.getState()));
transaction.attach(new PendingContactAddedEvent(p));
}
@Override

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.PrivateKey;
@@ -98,7 +97,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
static final int CODE_SCHEMA_VERSION = 44;
static final int CODE_SCHEMA_VERSION = 45;
// Time period offsets for incoming transport keys
private static final int OFFSET_PREV = -1;
@@ -264,7 +263,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (pendingContactId _HASH NOT NULL,"
+ " publicKey _BINARY NOT NULL,"
+ " alias _STRING NOT NULL,"
+ " state INT NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " PRIMARY KEY (pendingContactId))";
@@ -457,7 +455,8 @@ abstract class JdbcDatabase implements Database<Connection> {
new Migration40_41(dbTypes),
new Migration41_42(dbTypes),
new Migration42_43(dbTypes),
new Migration43_44(dbTypes)
new Migration43_44(dbTypes),
new Migration44_45()
);
}
@@ -933,14 +932,13 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO pendingContacts (pendingContactId,"
+ " publicKey, alias, state, timestamp)"
+ " VALUES (?, ?, ?, ?, ?)";
+ " publicKey, alias, timestamp)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getId().getBytes());
ps.setBytes(2, p.getPublicKey().getEncoded());
ps.setString(3, p.getAlias());
ps.setInt(4, p.getState().getValue());
ps.setLong(5, p.getTimestamp());
ps.setLong(4, p.getTimestamp());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -2213,8 +2211,7 @@ abstract class JdbcDatabase implements Database<Connection> {
Statement s = null;
ResultSet rs = null;
try {
String sql = "SELECT pendingContactId, publicKey, alias, state,"
+ " timestamp"
String sql = "SELECT pendingContactId, publicKey, alias, timestamp"
+ " FROM pendingContacts";
s = txn.createStatement();
rs = s.executeQuery(sql);
@@ -2223,11 +2220,9 @@ abstract class JdbcDatabase implements Database<Connection> {
PendingContactId id = new PendingContactId(rs.getBytes(1));
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(2));
String alias = rs.getString(3);
PendingContactState state =
PendingContactState.fromValue(rs.getInt(4));
long timestamp = rs.getLong(5);
long timestamp = rs.getLong(4);
pendingContacts.add(new PendingContact(id, publicKey, alias,
state, timestamp));
timestamp));
}
rs.close();
s.close();
@@ -3077,25 +3072,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void setPendingContactState(Connection txn, PendingContactId p,
PendingContactState state) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE pendingContacts SET state = ?"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, state.getValue());
ps.setBytes(2, p.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void setReorderingWindow(Connection txn, KeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)

View File

@@ -0,0 +1,39 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration44_45 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration44_45.class.getName());
@Override
public int getStartVersion() {
return 44;
}
@Override
public int getEndVersion() {
return 45;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE pendingContacts DROP COLUMN state");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -19,7 +19,6 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
@@ -108,7 +107,6 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
assertArrayEquals(publicKey.getEncoded(),
p.getPublicKey().getEncoded());
assertEquals(alias, p.getAlias());
assertEquals(WAITING_FOR_CONNECTION, p.getState());
assertEquals(timestamp, p.getTimestamp());
}

View File

@@ -56,7 +56,6 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
@@ -2211,16 +2210,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
PendingContact retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertEquals(pendingContact.getState(), retrieved.getState());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.setPendingContactState(txn, pendingContact.getId(), FAILED);
pendingContacts = db.getPendingContacts(txn);
assertEquals(1, pendingContacts.size());
retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertEquals(FAILED, retrieved.getState());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.removePendingContact(txn, pendingContact.getId());
@@ -2232,8 +2221,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test
public void testSetHandshakeKeyPair() throws Exception {
Identity withoutKeys =
new Identity(localAuthor, null, null, identity.getTimeCreated());
Identity withoutKeys = new Identity(localAuthor, null, null,
identity.getTimeCreated());
assertFalse(withoutKeys.hasHandshakeKeyPair());
PublicKey publicKey = getAgreementPublicKey();
PrivateKey privateKey = getAgreementPrivateKey();

View File

@@ -18,10 +18,9 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.event.Event;
@@ -65,7 +64,6 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn
import static android.support.v4.view.ViewCompat.getTransitionName;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@@ -293,15 +291,8 @@ public class ContactListFragment extends BaseFragment implements EventListener,
(ConversationMessageReceivedEvent) e;
ConversationMessageHeader h = p.getMessageHeader();
updateItem(p.getContactId(), h);
} else if (e instanceof PendingContactStateChangedEvent) {
PendingContactStateChangedEvent pe =
(PendingContactStateChangedEvent) e;
// only re-check pending contacts for initial state
if (pe.getPendingContactState() == WAITING_FOR_CONNECTION) {
checkForPendingContacts();
}
} else if (e instanceof PendingContactRemovedEvent ||
e instanceof ContactAddedRemotelyEvent) {
} else if (e instanceof PendingContactAddedEvent ||
e instanceof PendingContactRemovedEvent) {
checkForPendingContacts();
}
}

View File

@@ -4,7 +4,6 @@ import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.briarproject.bramble.api.FormatException;
@@ -45,7 +44,7 @@ public class AddContactViewModel extends AndroidViewModel {
private String remoteHandshakeLink;
@Inject
public AddContactViewModel(@NonNull Application application,
AddContactViewModel(Application application,
ContactManager contactManager,
@DatabaseExecutor Executor dbExecutor) {
super(application);

View File

@@ -0,0 +1,29 @@
package org.briarproject.briar.android.contact.add.remote;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class PendingContactItem {
private final PendingContact pendingContact;
private final PendingContactState state;
PendingContactItem(PendingContact pendingContact,
PendingContactState state) {
this.pendingContact = pendingContact;
this.state = state;
}
PendingContact getPendingContact() {
return pendingContact;
}
PendingContactState getState() {
return state;
}
}

View File

@@ -53,7 +53,8 @@ public class PendingContactListActivity extends BriarActivity
viewModel.getPendingContacts()
.observe(this, this::onPendingContactsChanged);
adapter = new PendingContactListAdapter(this, this, PendingContact.class);
adapter = new PendingContactListAdapter(this, this,
PendingContactItem.class);
list = findViewById(R.id.list);
list.setEmptyText(R.string.no_pending_contacts);
list.setLayoutManager(new LinearLayoutManager(this));
@@ -75,13 +76,11 @@ public class PendingContactListActivity extends BriarActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
@@ -89,8 +88,9 @@ public class PendingContactListActivity extends BriarActivity
viewModel.removePendingContact(pendingContact.getId());
}
private void onPendingContactsChanged(Collection<PendingContact> contacts) {
if (contacts.isEmpty()) {
private void onPendingContactsChanged(
Collection<PendingContactItem> items) {
if (items.isEmpty()) {
if (adapter.isEmpty()) {
list.showData(); // hides progress bar, shows empty text
} else {
@@ -98,7 +98,7 @@ public class PendingContactListActivity extends BriarActivity
supportFinishAfterTransition();
}
} else {
adapter.setItems(contacts);
adapter.setItems(items);
}
}

View File

@@ -12,12 +12,12 @@ import org.briarproject.briar.android.util.BriarAdapter;
@NotNullByDefault
class PendingContactListAdapter extends
BriarAdapter<PendingContact, PendingContactViewHolder> {
BriarAdapter<PendingContactItem, PendingContactViewHolder> {
private final PendingContactListener listener;
PendingContactListAdapter(Context ctx, PendingContactListener listener,
Class<PendingContact> c) {
Class<PendingContactItem> c) {
super(ctx, c);
this.listener = listener;
}
@@ -37,23 +37,29 @@ class PendingContactListAdapter extends
}
@Override
public int compare(PendingContact item1, PendingContact item2) {
return (int) (item1.getTimestamp() - item2.getTimestamp());
public int compare(PendingContactItem item1, PendingContactItem item2) {
long timestamp1 = item1.getPendingContact().getTimestamp();
long timestamp2 = item2.getPendingContact().getTimestamp();
return Long.compare(timestamp1, timestamp2);
}
@Override
public boolean areContentsTheSame(PendingContact item1,
PendingContact item2) {
return item1.getId().equals(item2.getId()) &&
item1.getAlias().equals(item2.getAlias()) &&
item1.getTimestamp() == item2.getTimestamp() &&
public boolean areContentsTheSame(PendingContactItem item1,
PendingContactItem item2) {
PendingContact p1 = item1.getPendingContact();
PendingContact p2 = item2.getPendingContact();
return p1.getId().equals(p2.getId()) &&
p1.getAlias().equals(p2.getAlias()) &&
p1.getTimestamp() == p2.getTimestamp() &&
item1.getState() == item2.getState();
}
@Override
public boolean areItemsTheSame(PendingContact item1,
PendingContact item2) {
return item1.getId().equals(item2.getId());
public boolean areItemsTheSame(PendingContactItem item1,
PendingContactItem item2) {
PendingContact p1 = item1.getPendingContact();
PendingContact p2 = item2.getPendingContact();
return p1.getId().equals(p2.getId());
}
}

View File

@@ -5,10 +5,11 @@ import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -18,7 +19,9 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -40,11 +43,11 @@ public class PendingContactListViewModel extends AndroidViewModel
private final ContactManager contactManager;
private final EventBus eventBus;
private final MutableLiveData<Collection<PendingContact>> pendingContacts =
new MutableLiveData<>();
private final MutableLiveData<Collection<PendingContactItem>>
pendingContacts = new MutableLiveData<>();
@Inject
public PendingContactListViewModel(Application application,
PendingContactListViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
ContactManager contactManager, EventBus eventBus) {
super(application);
@@ -63,8 +66,7 @@ public class PendingContactListViewModel extends AndroidViewModel
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedRemotelyEvent ||
e instanceof PendingContactStateChangedEvent ||
if (e instanceof PendingContactStateChangedEvent ||
e instanceof PendingContactRemovedEvent) {
loadPendingContacts();
}
@@ -73,14 +75,21 @@ public class PendingContactListViewModel extends AndroidViewModel
private void loadPendingContacts() {
dbExecutor.execute(() -> {
try {
pendingContacts.postValue(contactManager.getPendingContacts());
Collection<Pair<PendingContact, PendingContactState>> pairs =
contactManager.getPendingContacts();
List<PendingContactItem> items = new ArrayList<>(pairs.size());
for (Pair<PendingContact, PendingContactState> p : pairs) {
items.add(new PendingContactItem(p.getFirst(),
p.getSecond()));
}
pendingContacts.setValue(items);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
LiveData<Collection<PendingContact>> getPendingContacts() {
LiveData<Collection<PendingContactItem>> getPendingContacts() {
return pendingContacts;
}

View File

@@ -35,13 +35,14 @@ class PendingContactViewHolder extends ViewHolder {
this.listener = listener;
}
public void bind(PendingContact item) {
avatar.setText(item.getAlias());
avatar.setBackgroundBytes(item.getId().getBytes());
name.setText(item.getAlias());
time.setText(formatDate(time.getContext(), item.getTimestamp()));
public void bind(PendingContactItem item) {
PendingContact p = item.getPendingContact();
avatar.setText(p.getAlias());
avatar.setBackgroundBytes(p.getId().getBytes());
name.setText(p.getAlias());
time.setText(formatDate(time.getContext(), p.getTimestamp()));
removeButton.setOnClickListener(v -> {
listener.onFailedPendingContactRemoved(item);
listener.onFailedPendingContactRemoved(p);
removeButton.setEnabled(false);
});

View File

@@ -107,11 +107,27 @@ Until it is completed, a pending contact is returned as JSON:
{
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
"alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
"state": "adding_contact",
"timestamp": 1557838312175
}
```
It is possible to get a list of all pending contacts:
`GET /v1/contacts/add/pending`
This will return a JSON array of pending contacts and their states:
```json
{
"pendingContact": {
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
"alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
"timestamp": 1557838312175
},
"state": "adding_contact"
}
```
The state can be one of these values:
* `waiting_for_connection`
@@ -119,21 +135,16 @@ The state can be one of these values:
* `adding_contact`
* `failed`
If you want to get informed about state changes,
If you want to be informed about state changes,
you can use the Websocket API (below) to listen for events.
The following events are relevant here:
* `PendingContactAddedEvent`
* `PendingContactStateChangedEvent`
* `PendingContactRemovedEvent`
* `ContactAddedRemotelyEvent` (when the pending contact becomes an actual contact)
It is possible to get a list of all pending contacts:
`GET /v1/contacts/add/pending`
This will return a JSON array of pending contacts formatted as shown above.
To remove a pending contact and abort the process of adding it:
`DELETE /v1/contacts/add/pending`
@@ -302,6 +313,22 @@ it will send a JSON object representing the new contact to connected websocket c
}
```
### A pending contact was added
```json
{
"data": {
"pendingContact": {
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
"alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
"timestamp": 1557838312175
}
},
"name": "PendingContactAddedEvent",
"type": "event"
}
```
### A pending contact changed its state
```json

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.contact.ContactManager
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX
import org.briarproject.bramble.api.contact.PendingContactId
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
import org.briarproject.bramble.api.db.NoSuchContactException
@@ -28,6 +29,7 @@ import javax.inject.Singleton
internal const val EVENT_CONTACT_ADDED_REMOTELY = "ContactAddedRemotelyEvent"
internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent"
internal const val EVENT_PENDING_CONTACT_ADDED = "PendingContactAddedEvent"
internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent"
@Immutable
@@ -47,6 +49,9 @@ constructor(
is PendingContactStateChangedEvent -> {
webSocket.sendEvent(EVENT_PENDING_CONTACT_STATE_CHANGED, e.output())
}
is PendingContactAddedEvent -> {
webSocket.sendEvent(EVENT_PENDING_CONTACT_ADDED, e.output())
}
is PendingContactRemovedEvent -> {
webSocket.sendEvent(EVENT_PENDING_CONTACT_REMOVED, e.output())
}
@@ -78,8 +83,8 @@ constructor(
}
override fun listPendingContacts(ctx: Context): Context {
val pendingContacts = contactManager.pendingContacts.map { pendingContact ->
pendingContact.output()
val pendingContacts = contactManager.pendingContacts.map { pair ->
JsonDict("pendingContact" to pair.first.output(), "state" to pair.second.output())
}
return ctx.json(pendingContacts)
}

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.headless.contact
import org.briarproject.bramble.api.contact.PendingContact
import org.briarproject.bramble.api.contact.PendingContactState
import org.briarproject.bramble.api.contact.PendingContactState.*
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
import org.briarproject.briar.headless.json.JsonDict
@@ -10,7 +11,6 @@ import org.briarproject.briar.headless.json.JsonDict
internal fun PendingContact.output() = JsonDict(
"pendingContactId" to id.bytes,
"alias" to alias,
"state" to state.output(),
"timestamp" to timestamp
)
@@ -22,6 +22,10 @@ internal fun PendingContactState.output() = when(this) {
else -> throw AssertionError()
}
internal fun PendingContactAddedEvent.output() = JsonDict(
"pendingContact" to pendingContact.output()
)
internal fun PendingContactStateChangedEvent.output() = JsonDict(
"pendingContactId" to id.bytes,
"state" to pendingContactState.output()

View File

@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test
class ContactControllerIntegrationTest: IntegrationTest() {
@Test
fun `list of contacts need authentication token`() {
fun `returning list of contacts needs authentication token`() {
val response = getWithWrongToken("$url/contacts")
assertEquals(401, response.statusCode)
}
@@ -72,11 +72,10 @@ class ContactControllerIntegrationTest: IntegrationTest() {
assertEquals(200, response.statusCode)
assertEquals(1, response.jsonArray.length())
val jsonObject = response.jsonArray.getJSONObject(0)
assertEquals(alias, jsonObject.getString("alias"))
assertEquals("waiting_for_connection", jsonObject.getString("state"))
assertEquals(alias, jsonObject.getJSONObject("pendingContact").getString("alias"))
// remove pending contact again
val idString = jsonObject.getString("pendingContactId")
val idString = jsonObject.getJSONObject("pendingContact").getString("pendingContactId")
val deleteJson = """{"pendingContactId": "$idString"}"""
response = delete("$url/contacts/add/pending", deleteJson)
assertEquals(200, response.statusCode)
@@ -94,7 +93,7 @@ class ContactControllerIntegrationTest: IntegrationTest() {
}
@Test
fun `adding pending contacts needs authentication token`() {
fun `adding a pending contact needs authentication token`() {
val response = postWithWrongToken("$url/contacts/add/pending")
assertEquals(401, response.statusCode)
}
@@ -106,7 +105,7 @@ class ContactControllerIntegrationTest: IntegrationTest() {
}
@Test
fun `deleting contact need authentication token`() {
fun `deleting a contact needs authentication token`() {
val response = deleteWithWrongToken("$url/contacts/1")
assertEquals(401, response.statusCode)
}

View File

@@ -7,11 +7,14 @@ import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.runs
import org.briarproject.bramble.api.Pair
import org.briarproject.bramble.api.contact.Contact
import org.briarproject.bramble.api.contact.ContactId
import org.briarproject.bramble.api.contact.PendingContactId
import org.briarproject.bramble.api.contact.PendingContactState.FAILED
import org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
import org.briarproject.bramble.api.db.NoSuchContactException
@@ -124,8 +127,14 @@ internal class ContactControllerTest : ControllerTest() {
@Test
fun testListPendingContacts() {
every { contactManager.pendingContacts } returns listOf(pendingContact)
every { ctx.json(listOf(pendingContact.output())) } returns ctx
every { contactManager.pendingContacts } returns listOf(
Pair(pendingContact, WAITING_FOR_CONNECTION)
)
val dict = JsonDict(
"pendingContact" to pendingContact.output(),
"state" to WAITING_FOR_CONNECTION.output()
)
every { ctx.json(listOf(dict)) } returns ctx
controller.listPendingContacts(ctx)
}
@@ -225,6 +234,20 @@ internal class ContactControllerTest : ControllerTest() {
controller.eventOccurred(event)
}
@Test
fun testPendingContactAddedEvent() {
val event = PendingContactAddedEvent(pendingContact)
every {
webSocketController.sendEvent(
EVENT_PENDING_CONTACT_ADDED,
event.output()
)
} just runs
controller.eventOccurred(event)
}
@Test
fun testPendingContactRemovedEvent() {
val event = PendingContactRemovedEvent(pendingContact.id)
@@ -284,13 +307,27 @@ internal class ContactControllerTest : ControllerTest() {
{
"pendingContactId": ${toJson(pendingContact.id.bytes)},
"alias": "${pendingContact.alias}",
"state": "${pendingContact.state.name.toLowerCase()}",
"timestamp": ${pendingContact.timestamp}
}
"""
assertJsonEquals(json, pendingContact.output())
}
@Test
fun testOutputPendingContactAddedEvent() {
val event = PendingContactAddedEvent(pendingContact)
val json = """
{
"pendingContact": {
"pendingContactId": ${toJson(pendingContact.id.bytes)},
"alias": "${pendingContact.alias}",
"timestamp": ${pendingContact.timestamp}
}
}
"""
assertJsonEquals(json, event.output())
}
@Test
fun testOutputPendingContactStateChangedEvent() {
val event = PendingContactStateChangedEvent(pendingContact.id, FAILED)