syncbase java high-level: more bits of implementation
Change-Id: Ife7195c5903699a0086fd0504ebb45a7736835ff
diff --git a/syncbase/build.gradle b/syncbase/build.gradle
index a89a3d1..ec0a3fd 100644
--- a/syncbase/build.gradle
+++ b/syncbase/build.gradle
@@ -34,12 +34,15 @@
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ lintOptions {
+ abortOnError false
+ }
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.4.0'
- compile 'io.v:vanadium-android:2.1.3'
+ compile 'io.v:vanadium-android:2.1.4'
testCompile 'junit:junit:4.12'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
diff --git a/syncbase/src/main/java/io/v/syncbase/AccessList.java b/syncbase/src/main/java/io/v/syncbase/AccessList.java
index 3b7e158..b2304a2 100644
--- a/syncbase/src/main/java/io/v/syncbase/AccessList.java
+++ b/syncbase/src/main/java/io/v/syncbase/AccessList.java
@@ -4,7 +4,14 @@
package io.v.syncbase;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+
+import io.v.v23.security.BlessingPattern;
+import io.v.v23.security.access.Constants;
+import io.v.v23.security.access.Permissions;
public class AccessList {
public enum AccessLevel {
@@ -14,4 +21,84 @@
}
public Map<String, AccessLevel> users;
+
+ private static Set<String> vAccessListToUserIds(io.v.v23.security.access.AccessList accessList) {
+ if (!accessList.getNotIn().isEmpty()) {
+ throw new RuntimeException("Non-empty not-in section: " + accessList);
+ }
+ Set<String> res = new HashSet<>();
+ for (BlessingPattern bp : accessList.getIn()) {
+ res.add(Syncbase.getEmailFromBlessingPattern(bp));
+ }
+ return res;
+ }
+
+ public AccessList() {
+ this.users = new HashMap<>();
+ }
+
+ protected AccessList(Permissions perms) {
+ Set<String> readers = vAccessListToUserIds(perms.get(Constants.READ.getValue()));
+ Set<String> writers = vAccessListToUserIds(perms.get(Constants.WRITE.getValue()));
+ Set<String> admins = vAccessListToUserIds(perms.get(Constants.ADMIN.getValue()));
+ if (!readers.containsAll(writers)) {
+ throw new RuntimeException("Some writers are not readers: " + writers + ", " + readers);
+ }
+ if (!writers.containsAll(admins)) {
+ throw new RuntimeException("Some admins are not writers: " + admins + ", " + writers);
+ }
+ for (String userId : readers) {
+ users.put(userId, AccessLevel.READ);
+ }
+ for (String userId : writers) {
+ users.put(userId, AccessLevel.READ_WRITE);
+ }
+ for (String userId : admins) {
+ users.put(userId, AccessLevel.READ_WRITE_ADMIN);
+ }
+ }
+
+ private static void addToVAccessList(io.v.v23.security.access.AccessList accessList, BlessingPattern bp) {
+ if (!accessList.getIn().contains(bp)) {
+ accessList.getIn().add(bp);
+ }
+ }
+
+ private static void removeFromVAccessList(io.v.v23.security.access.AccessList accessList, BlessingPattern bp) {
+ accessList.getIn().remove(bp);
+ }
+
+ protected static void applyDelta(Permissions perms, AccessList delta) {
+ for (String userId : delta.users.keySet()) {
+ AccessLevel level = delta.users.get(userId);
+ BlessingPattern bp = Syncbase.getBlessingPatternFromEmail(userId);
+ if (level == null) {
+ removeFromVAccessList(perms.get(Constants.RESOLVE.getValue()), bp);
+ removeFromVAccessList(perms.get(Constants.READ.getValue()), bp);
+ removeFromVAccessList(perms.get(Constants.WRITE.getValue()), bp);
+ removeFromVAccessList(perms.get(Constants.ADMIN.getValue()), bp);
+ } else {
+ switch (level) {
+ case READ:
+ addToVAccessList(perms.get(Constants.RESOLVE.getValue()), bp);
+ addToVAccessList(perms.get(Constants.READ.getValue()), bp);
+ removeFromVAccessList(perms.get(Constants.WRITE.getValue()), bp);
+ removeFromVAccessList(perms.get(Constants.ADMIN.getValue()), bp);
+ break;
+ case READ_WRITE:
+ addToVAccessList(perms.get(Constants.RESOLVE.getValue()), bp);
+ addToVAccessList(perms.get(Constants.READ.getValue()), bp);
+ addToVAccessList(perms.get(Constants.WRITE.getValue()), bp);
+ removeFromVAccessList(perms.get(Constants.ADMIN.getValue()), bp);
+ break;
+ case READ_WRITE_ADMIN:
+ addToVAccessList(perms.get(Constants.RESOLVE.getValue()), bp);
+ addToVAccessList(perms.get(Constants.READ.getValue()), bp);
+ addToVAccessList(perms.get(Constants.WRITE.getValue()), bp);
+ addToVAccessList(perms.get(Constants.ADMIN.getValue()), bp);
+ break;
+ }
+ }
+ }
+ }
}
diff --git a/syncbase/src/main/java/io/v/syncbase/BatchDatabase.java b/syncbase/src/main/java/io/v/syncbase/BatchDatabase.java
index c231e29..26681bb 100644
--- a/syncbase/src/main/java/io/v/syncbase/BatchDatabase.java
+++ b/syncbase/src/main/java/io/v/syncbase/BatchDatabase.java
@@ -4,34 +4,21 @@
package io.v.syncbase;
-import java.util.Iterator;
-
-public class BatchDatabase implements DatabaseHandle {
- private final Database mDatabase;
+public class BatchDatabase extends DatabaseHandle {
private final io.v.v23.syncbase.BatchDatabase mVBatchDatabase;
- protected BatchDatabase(Database database, io.v.v23.syncbase.BatchDatabase vBatchDatabase) {
- mDatabase = database;
+ protected BatchDatabase(io.v.v23.syncbase.BatchDatabase vBatchDatabase) {
+ super(vBatchDatabase);
mVBatchDatabase = vBatchDatabase;
}
- public Id getId() {
- return new Id(mVBatchDatabase.id());
- }
-
public Collection collection(String name, CollectionOptions opts) {
if (!opts.withoutSyncgroup) {
throw new RuntimeException("Cannot create syncgroup in a batch");
}
- return new Collection(mDatabase, mVBatchDatabase.getCollection(new io.v.v23.services.syncbase.Id(Syncbase.getPersonalBlessingString(), name)), true);
- }
-
- public Collection getCollection(Id id) {
- return Database.getCollectionImpl(mDatabase, mVBatchDatabase, id);
- }
-
- public Iterator<Collection> getCollections() {
- return Database.getCollectionsImpl(mDatabase, mVBatchDatabase);
+ Collection res = getCollection(new Id(Syncbase.getPersonalBlessingString(), name));
+ res.createIfMissing();
+ return res;
}
public void commit() {
diff --git a/syncbase/src/main/java/io/v/syncbase/Collection.java b/syncbase/src/main/java/io/v/syncbase/Collection.java
index 5dea588..af8503b 100644
--- a/syncbase/src/main/java/io/v/syncbase/Collection.java
+++ b/syncbase/src/main/java/io/v/syncbase/Collection.java
@@ -4,29 +4,28 @@
package io.v.syncbase;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-
import io.v.v23.VFutures;
+import io.v.v23.security.access.Permissions;
import io.v.v23.verror.ExistException;
import io.v.v23.verror.VException;
public class Collection {
- private final Database mDatabase;
private final io.v.v23.syncbase.Collection mVCollection;
+ private final DatabaseHandle mDatabaseHandle;
- protected Collection(Database database, io.v.v23.syncbase.Collection vCollection, boolean createIfMissing) {
- if (createIfMissing) {
- try {
- VFutures.sync(vCollection.create(Syncbase.getVContext(), null));
- } catch (ExistException e) {
- // Collection already exists.
- } catch (VException e) {
- throw new RuntimeException("Failed to create collection", e);
- }
+ protected void createIfMissing() {
+ try {
+ VFutures.sync(mVCollection.create(Syncbase.getVContext(), Syncbase.defaultPerms()));
+ } catch (ExistException e) {
+ // Collection already exists.
+ } catch (VException e) {
+ throw new RuntimeException("Failed to create collection", e);
}
- mDatabase = database;
+ }
+
+ protected Collection(io.v.v23.syncbase.Collection vCollection, DatabaseHandle databaseHandle) {
mVCollection = vCollection;
+ mDatabaseHandle = databaseHandle;
}
public Id getId() {
@@ -36,16 +35,18 @@
// Shortcut for Database.getSyncgroup(c.getId()), helpful for the common case of one syncgroup
// per collection.
public Syncgroup getSyncgroup() {
- return mDatabase.getSyncgroup(getId());
+ if (mDatabaseHandle instanceof BatchDatabase) {
+ throw new RuntimeException("Must not call getSyncgroup within batch");
+ }
+ return ((Database) mDatabaseHandle).getSyncgroup(getId());
}
// TODO(sadovsky): Maybe add scan API, if developers aren't satisfied with watch.
- // TODO(sadovsky): Revisit this API, which was copied from io.v.v23.syncbase. For example, would
- // the signature "public <T> T get(String key)" be preferable?
- public Object get(String key, Type type) {
+ // TODO(sadovsky): Revisit this API. Is the Class<T> argument necessary?
+ public <T> T get(String key, Class<T> cls) {
try {
- return VFutures.sync(mVCollection.getRow(key).get(Syncbase.getVContext(), type));
+ return VFutures.sync(mVCollection.getRow(key).get(Syncbase.getVContext(), cls));
} catch (VException e) {
throw new RuntimeException("get failed: " + key, e);
}
@@ -59,19 +60,9 @@
}
}
- // TODO(sadovsky): Only needed for the current (old) version of io.v.v23.syncbase, which does
- // not include fredq's change to the put() API.
- private static Type getType(Object object) {
- Type superclassType = object.getClass().getGenericSuperclass();
- if (!ParameterizedType.class.isAssignableFrom(superclassType.getClass())) {
- return null;
- }
- return ((ParameterizedType) superclassType).getActualTypeArguments()[0];
- }
-
public <T> void put(String key, T value) {
try {
- VFutures.sync(mVCollection.put(Syncbase.getVContext(), key, value, getType(value)));
+ VFutures.sync(mVCollection.put(Syncbase.getVContext(), key, value));
} catch (VException e) {
throw new RuntimeException("put failed: " + key, e);
}
@@ -88,10 +79,38 @@
// FOR ADVANCED USERS. The following methods manipulate the AccessList for this collection, but
// not for associated syncgroups.
public AccessList getAccessList() {
- throw new RuntimeException("Not implemented");
+ try {
+ return new AccessList(VFutures.sync(mVCollection.getPermissions(Syncbase.getVContext())));
+ } catch (VException e) {
+ throw new RuntimeException("getPermissions failed", e);
+ }
}
- public void updateAccessList(AccessList delta) {
- throw new RuntimeException("Not implemented");
+ public void updateAccessList(final AccessList delta) {
+ // Create a batch if we're not already in a batch.
+ final Id id = this.getId();
+ Database.BatchOperation op = new Database.BatchOperation() {
+ @Override
+ public void run(BatchDatabase db) {
+ io.v.v23.syncbase.Collection vCx = db.getCollection(id).mVCollection;
+ Permissions perms;
+ try {
+ perms = VFutures.sync(vCx.getPermissions(Syncbase.getVContext()));
+ } catch (VException e) {
+ throw new RuntimeException("getPermissions failed", e);
+ }
+ AccessList.applyDelta(perms, delta);
+ try {
+ VFutures.sync(vCx.setPermissions(Syncbase.getVContext(), perms));
+ } catch (VException e) {
+ throw new RuntimeException("setPermissions failed", e);
+ }
+ }
+ };
+ if (mDatabaseHandle instanceof BatchDatabase) {
+ op.run((BatchDatabase) mDatabaseHandle);
+ } else {
+ ((Database) mDatabaseHandle).runInBatch(op, new Database.BatchOptions());
+ }
}
}
diff --git a/syncbase/src/main/java/io/v/syncbase/Database.java b/syncbase/src/main/java/io/v/syncbase/Database.java
index 2e9236e..f03cba0 100644
--- a/syncbase/src/main/java/io/v/syncbase/Database.java
+++ b/syncbase/src/main/java/io/v/syncbase/Database.java
@@ -9,61 +9,33 @@
import java.util.List;
import io.v.v23.VFutures;
-import io.v.v23.syncbase.DatabaseCore;
import io.v.v23.verror.ExistException;
import io.v.v23.verror.VException;
-public class Database implements DatabaseHandle {
+public class Database extends DatabaseHandle {
private final io.v.v23.syncbase.Database mVDatabase;
- protected Database(io.v.v23.syncbase.Database vDatabase) {
+ protected void createIfMissing() {
try {
- VFutures.sync(vDatabase.create(Syncbase.getVContext(), Syncbase.defaultPerms()));
+ VFutures.sync(mVDatabase.create(Syncbase.getVContext(), Syncbase.defaultPerms()));
} catch (ExistException e) {
// Database already exists, presumably from a previous run of the app.
} catch (VException e) {
throw new RuntimeException("Failed to create database", e);
}
+ }
+
+ protected Database(io.v.v23.syncbase.Database vDatabase) {
+ super(vDatabase);
mVDatabase = vDatabase;
}
- public Id getId() {
- return new Id(mVDatabase.id());
- }
-
public Collection collection(String name, CollectionOptions opts) {
- // TODO(sadovsky): If !opts.withoutSyncgroup, create syncgroup and update userdata syncgroup.
- return new Collection(this, mVDatabase.getCollection(new io.v.v23.services.syncbase.Id(Syncbase.getPersonalBlessingString(), name)), true);
- }
-
- protected static Collection getCollectionImpl(Database database, DatabaseCore vDbCore, Id id) {
- // TODO(sadovsky): Consider throwing an exception or returning null if the collection does
- // not exist.
- return new Collection(database, vDbCore.getCollection(id.toVId()), false);
- }
-
- public Collection getCollection(Id id) {
- return getCollectionImpl(this, mVDatabase, id);
- }
-
- // Exposed as a static function so that the implementation can be shared between Database and
- // BatchDatabase.
- protected static Iterator<Collection> getCollectionsImpl(Database database, DatabaseCore vDbCore) {
- List<io.v.v23.services.syncbase.Id> vIds;
- try {
- vIds = VFutures.sync(vDbCore.listCollections(Syncbase.getVContext()));
- } catch (VException e) {
- throw new RuntimeException("listCollections failed", e);
- }
- ArrayList<Collection> cxs = new ArrayList<>(vIds.size());
- for (io.v.v23.services.syncbase.Id vId : vIds) {
- cxs.add(new Collection(database, vDbCore.getCollection(vId), false));
- }
- return cxs.iterator();
- }
-
- public Iterator<Collection> getCollections() {
- return getCollectionsImpl(this, mVDatabase);
+ // TODO(sadovsky): If !opts.withoutSyncgroup, create syncgroup and update userdata
+ // syncgroup.
+ Collection res = getCollection(new Id(Syncbase.getPersonalBlessingString(), name));
+ res.createIfMissing();
+ return res;
}
public static class SyncgroupOptions {
@@ -72,24 +44,50 @@
// FOR ADVANCED USERS. Creates syncgroup and adds it to the user's "userdata" collection, as
// needed. Idempotent.
- public Syncgroup syncgroup(String name, Collection[] collections, SyncgroupOptions opts) {
- throw new RuntimeException("Not implemented");
+ public Syncgroup syncgroup(String name, List<Collection> collections, SyncgroupOptions opts) {
+ Id id = new Id(collections.get(0).getId().getBlessing(), name);
+ Syncgroup res = new Syncgroup(mVDatabase.getSyncgroup(id.toVId()), this, id);
+ res.createIfMissing(collections);
+ return res;
}
+ public Syncgroup syncgroup(String name, List<Collection> collections) {
+ return syncgroup(name, collections, new SyncgroupOptions());
+ }
+
+
public Syncgroup getSyncgroup(Id id) {
// TODO(sadovsky): Consider throwing an exception or returning null if the syncgroup does
// not exist.
- return new Syncgroup(id, mVDatabase.getSyncgroup(id.toVId()));
+ return new Syncgroup(mVDatabase.getSyncgroup(id.toVId()), this, id);
}
public Iterator<Syncgroup> getSyncgroups() {
- throw new RuntimeException("Not implemented");
+ List<io.v.v23.services.syncbase.Id> vIds;
+ try {
+ vIds = VFutures.sync(mVDatabase.listSyncgroups(Syncbase.getVContext()));
+ } catch (VException e) {
+ throw new RuntimeException("listSyncgroups failed", e);
+ }
+ ArrayList<Syncgroup> sgs = new ArrayList<>(vIds.size());
+ for (io.v.v23.services.syncbase.Id vId : vIds) {
+ sgs.add(new Syncgroup(mVDatabase.getSyncgroup(vId), this, new Id(vId)));
+ }
+ return sgs.iterator();
}
public static class AddSyncgroupInviteHandlerOptions {
// TODO(sadovsky): Fill this in.
}
+ public abstract class SyncgroupInviteHandler {
+ void onInvite(SyncgroupInvite invite) {
+ }
+
+ void onError(Exception e) {
+ }
+ }
+
// Notifies 'h' of any existing syncgroup invites, and of all subsequent new invites.
public void addSyncgroupInviteHandler(SyncgroupInviteHandler h, AddSyncgroupInviteHandlerOptions opts) {
throw new RuntimeException("Not implemented");
@@ -125,6 +123,14 @@
}
}
+ public interface BatchOperation {
+ void run(BatchDatabase db);
+ }
+
+ public void runInBatch(BatchOperation op, BatchOptions opts) {
+ throw new RuntimeException("Not implemented");
+ }
+
public BatchDatabase beginBatch(BatchOptions opts) {
io.v.v23.syncbase.BatchDatabase vBatchDatabase;
try {
@@ -132,13 +138,29 @@
} catch (VException e) {
throw new RuntimeException("beginBatch failed", e);
}
- return new BatchDatabase(this, vBatchDatabase);
+ return new BatchDatabase(vBatchDatabase);
}
public static class AddWatchChangeHandlerOptions {
public byte[] resumeMarker;
}
+ public abstract class WatchChangeHandler {
+ // TODO(sadovsky): Consider adopting Aaron's suggestion of combining onInitialState and
+ // onChangeBatch into a single method, to make things simpler for developers who don't want to
+ // apply deltas to their in-memory data structures:
+ // void onChangeBatch(Iterator<WatchChange> values, Iterator<WatchChange> changes)
+
+ void onInitialState(Iterator<WatchChange> values) {
+ }
+
+ void onChangeBatch(Iterator<WatchChange> changes) {
+ }
+
+ void onError(Exception e) {
+ }
+ }
+
// Notifies 'h' of initial state, and of all subsequent changes to this database.
// Note: Eventually we'll add a watch variant that takes a query, where the query can be
// constructed using some sort of query builder API.
diff --git a/syncbase/src/main/java/io/v/syncbase/DatabaseHandle.java b/syncbase/src/main/java/io/v/syncbase/DatabaseHandle.java
index bf986a6..a18c721 100644
--- a/syncbase/src/main/java/io/v/syncbase/DatabaseHandle.java
+++ b/syncbase/src/main/java/io/v/syncbase/DatabaseHandle.java
@@ -4,18 +4,52 @@
package io.v.syncbase;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
-public interface DatabaseHandle {
- Id getId();
+import io.v.v23.VFutures;
+import io.v.v23.syncbase.DatabaseCore;
+import io.v.v23.verror.VException;
+
+public abstract class DatabaseHandle {
+ protected DatabaseCore mVDatabaseCore;
+
+ protected DatabaseHandle(DatabaseCore vDatabaseCore) {
+ mVDatabaseCore = vDatabaseCore;
+ }
+
+ Id getId() {
+ return new Id(mVDatabaseCore.id());
+ }
class CollectionOptions {
public boolean withoutSyncgroup;
}
- Collection collection(String name, CollectionOptions opts);
+ abstract Collection collection(String name, CollectionOptions opts);
- Collection getCollection(Id id);
+ Collection collection(String name) {
+ return collection(name, new CollectionOptions());
+ }
- Iterator<Collection> getCollections();
+ Collection getCollection(Id id) {
+ // TODO(sadovsky): Consider throwing an exception or returning null if the collection does
+ // not exist.
+ return new Collection(mVDatabaseCore.getCollection(id.toVId()), this);
+ }
+
+ Iterator<Collection> getCollections() {
+ List<io.v.v23.services.syncbase.Id> vIds;
+ try {
+ vIds = VFutures.sync(mVDatabaseCore.listCollections(Syncbase.getVContext()));
+ } catch (VException e) {
+ throw new RuntimeException("listCollections failed", e);
+ }
+ ArrayList<Collection> cxs = new ArrayList<>(vIds.size());
+ for (io.v.v23.services.syncbase.Id vId : vIds) {
+ cxs.add(new Collection(mVDatabaseCore.getCollection(vId), this));
+ }
+ return cxs.iterator();
+ }
}
diff --git a/syncbase/src/main/java/io/v/syncbase/Id.java b/syncbase/src/main/java/io/v/syncbase/Id.java
index d34e1a7..4908bfb 100644
--- a/syncbase/src/main/java/io/v/syncbase/Id.java
+++ b/syncbase/src/main/java/io/v/syncbase/Id.java
@@ -8,6 +8,15 @@
private final String mBlessing;
private final String mName;
+ protected Id(String blessing, String name) {
+ mBlessing = blessing;
+ mName = name;
+ }
+
+ protected String getBlessing() {
+ return mBlessing;
+ }
+
// TODO(sadovsky): Eliminate the code below once we've switched to io.v.syncbase.core.
protected Id(io.v.v23.services.syncbase.Id id) {
diff --git a/syncbase/src/main/java/io/v/syncbase/Syncbase.java b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
index dfd9547..c3624ac 100644
--- a/syncbase/src/main/java/io/v/syncbase/Syncbase.java
+++ b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
@@ -71,11 +71,12 @@
}
// TODO(sadovsky): Some of these constants should become fields in DatabaseOptions.
- private static final String
+ protected static final String
PROXY = "proxy",
DEFAULT_BLESSING_STRING_PREFIX = "dev.v.io:o:608941808256-43vtfndets79kf5hac8ieujto8837660.apps.googleusercontent.com:",
MOUNT_POINT = "/ns.dev.v.io:8101/tmp/todos/users/",
- CLOUD_BLESSING_STRING = "dev.v.io:u:alexfandrianto@google.com";
+ CLOUD_BLESSING_STRING = "dev.v.io:u:alexfandrianto@google.com",
+ CLOUD_NAME = MOUNT_POINT + "cloud";
private static Database startSyncbaseAndInitDatabase(VContext ctx) {
SyncbaseService s;
@@ -85,7 +86,9 @@
throw new RuntimeException("Failed to start Syncbase", e);
}
// Create database, if needed.
- return new Database(s.getDatabase(getVContext(), DB_NAME, null));
+ Database res = new Database(s.getDatabase(getVContext(), DB_NAME, null));
+ res.createIfMissing();
+ return res;
}
private static String startSyncbase(VContext vContext, String rootDir)
@@ -118,7 +121,7 @@
return getEmailFromBlessingString(blessings.toString());
}
- private static String getEmailFromBlessingPattern(BlessingPattern pattern) {
+ protected static String getEmailFromBlessingPattern(BlessingPattern pattern) {
return getEmailFromBlessingString(pattern.toString());
}
@@ -131,6 +134,10 @@
return DEFAULT_BLESSING_STRING_PREFIX + email;
}
+ protected static BlessingPattern getBlessingPatternFromEmail(String email) {
+ return new BlessingPattern(getBlessingStringFromEmail(email));
+ }
+
private static Blessings getPersonalBlessings() {
return V.getPrincipal(getVContext()).blessingStore().defaultBlessings();
}
@@ -147,21 +154,23 @@
protected static Permissions defaultPerms() {
// TODO(sadovsky): Revisit these default perms, which were copied from the Todos app.
- io.v.v23.security.access.AccessList openAccessList =
+
+
+ io.v.v23.security.access.AccessList anyone =
new io.v.v23.security.access.AccessList(
ImmutableList.of(
new BlessingPattern("...")),
ImmutableList.<String>of());
- io.v.v23.security.access.AccessList accessList =
+ io.v.v23.security.access.AccessList selfAndCloud =
new io.v.v23.security.access.AccessList(
ImmutableList.of(
new BlessingPattern(getPersonalBlessingString()),
new BlessingPattern(CLOUD_BLESSING_STRING)),
ImmutableList.<String>of());
return new Permissions(ImmutableMap.of(
- Constants.RESOLVE.getValue(), openAccessList,
- Constants.READ.getValue(), accessList,
- Constants.WRITE.getValue(), accessList,
- Constants.ADMIN.getValue(), accessList));
+ Constants.RESOLVE.getValue(), anyone,
+ Constants.READ.getValue(), selfAndCloud,
+ Constants.WRITE.getValue(), selfAndCloud,
+ Constants.ADMIN.getValue(), selfAndCloud));
}
}
\ No newline at end of file
diff --git a/syncbase/src/main/java/io/v/syncbase/Syncgroup.java b/syncbase/src/main/java/io/v/syncbase/Syncgroup.java
index 4a5009c..8be7b05 100644
--- a/syncbase/src/main/java/io/v/syncbase/Syncgroup.java
+++ b/syncbase/src/main/java/io/v/syncbase/Syncgroup.java
@@ -4,14 +4,51 @@
package io.v.syncbase;
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import io.v.v23.VFutures;
+import io.v.v23.services.syncbase.SyncgroupMemberInfo;
+import io.v.v23.services.syncbase.SyncgroupSpec;
+import io.v.v23.verror.ExistException;
+import io.v.v23.verror.VException;
+
public class Syncgroup {
+ private final Database mDatabase;
private final Id mId;
private final io.v.v23.syncbase.Syncgroup mVSyncgroup;
+ protected void createIfMissing(List<Collection> collections) {
+ ArrayList<io.v.v23.services.syncbase.Id> cxVIds = new ArrayList<>(collections.size());
+ for (Collection cx : collections) {
+ cxVIds.add(cx.getId().toVId());
+ }
+ SyncgroupSpec spec = new SyncgroupSpec(
+ "", Syncbase.CLOUD_NAME, Syncbase.defaultPerms(), cxVIds,
+ ImmutableList.of(Syncbase.MOUNT_POINT), false);
+ SyncgroupMemberInfo info = new SyncgroupMemberInfo();
+ // TODO(sadovsky): Still have no idea how to set sync priority.
+ info.setSyncPriority((byte) 3);
+ try {
+ VFutures.sync(mVSyncgroup.create(Syncbase.getVContext(), spec, info));
+ } catch (ExistException e) {
+ // Syncgroup already exists.
+ // TODO(sadovsky): Verify that the existing syncgroup has the specified configuration,
+ // e.g. the specified collections?
+ } catch (VException e) {
+ throw new RuntimeException("Failed to create collection", e);
+ }
+ }
+
// Note, we take 'id' because io.v.v23.syncbase.Syncgroup is missing a 'getId' method.
- protected Syncgroup(Id id, io.v.v23.syncbase.Syncgroup vSyncgroup) {
- mId = id;
+ protected Syncgroup(io.v.v23.syncbase.Syncgroup vSyncgroup, Database database, Id id) {
mVSyncgroup = vSyncgroup;
+ mDatabase = database;
+ mId = id;
}
public Id getId() {
@@ -19,7 +56,13 @@
}
public AccessList getAccessList() {
- throw new RuntimeException("Not implemented");
+ Map<String, SyncgroupSpec> versionedSpec;
+ try {
+ versionedSpec = VFutures.sync(mVSyncgroup.getSpec(Syncbase.getVContext()));
+ } catch (VException e) {
+ throw new RuntimeException("getSpec failed", e);
+ }
+ return new AccessList(versionedSpec.values().iterator().next().getPerms());
}
public static class UpdateAccessListOptions {
@@ -29,24 +72,63 @@
// The following methods update the AccessList for the syncgroup and its associated collections.
// Setting opts.syncgroupOnly makes it so these methods only update the AccessList for the
// syncgroup.
- public void addUsers(User[] users, AccessList.AccessLevel level, UpdateAccessListOptions opts) {
+ public void addUsers(List<User> users, AccessList.AccessLevel level, UpdateAccessListOptions opts) {
AccessList delta = new AccessList();
- for (User u: users) {
+ for (User u : users) {
delta.users.put(u.getId(), level);
}
updateAccessList(delta, opts);
}
- public void removeUsers(User[] users, UpdateAccessListOptions opts) {
+ public void addUsers(List<User> users, AccessList.AccessLevel level) {
+ addUsers(users, level, new UpdateAccessListOptions());
+ }
+
+ public void addUser(User user, AccessList.AccessLevel level) {
+ addUsers(Collections.singletonList(user), level);
+ }
+
+ public void removeUsers(List<User> users, UpdateAccessListOptions opts) {
AccessList delta = new AccessList();
- for (User u: users) {
+ for (User u : users) {
delta.users.put(u.getId(), null);
}
updateAccessList(delta, opts);
}
+ public void removeUsers(List<User> users) {
+ removeUsers(users, new UpdateAccessListOptions());
+ }
+
+ public void removeUser(User user) {
+ removeUsers(Collections.singletonList(user));
+ }
+
// Applies 'delta' to the AccessList. Note, NULL enum means "remove".
- public void updateAccessList(AccessList delta, UpdateAccessListOptions opts) {
- throw new RuntimeException("Not implemented");
+ public void updateAccessList(final AccessList delta, UpdateAccessListOptions opts) {
+ // TODO(sadovsky): Make it so SyncgroupSpec can be updated as part of a batch?
+ Map<String, SyncgroupSpec> versionedSpec;
+ try {
+ versionedSpec = VFutures.sync(mVSyncgroup.getSpec(Syncbase.getVContext()));
+ } catch (VException e) {
+ throw new RuntimeException("getSpec failed", e);
+ }
+ String version = versionedSpec.keySet().iterator().next();
+ SyncgroupSpec spec = versionedSpec.values().iterator().next();
+ AccessList.applyDelta(spec.getPerms(), delta);
+ try {
+ VFutures.sync(mVSyncgroup.setSpec(Syncbase.getVContext(), spec, version));
+ } catch (VException e) {
+ throw new RuntimeException("setSpec failed", e);
+ }
+ final List<io.v.v23.services.syncbase.Id> cxVIds = spec.getCollections();
+ mDatabase.runInBatch(new Database.BatchOperation() {
+ @Override
+ public void run(BatchDatabase db) {
+ for (io.v.v23.services.syncbase.Id vId : cxVIds) {
+ db.getCollection(new Id(vId)).updateAccessList(delta);
+ }
+ }
+ }, new Database.BatchOptions());
}
}
diff --git a/syncbase/src/main/java/io/v/syncbase/SyncgroupInviteHandler.java b/syncbase/src/main/java/io/v/syncbase/SyncgroupInviteHandler.java
deleted file mode 100644
index 3a95721..0000000
--- a/syncbase/src/main/java/io/v/syncbase/SyncgroupInviteHandler.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package io.v.syncbase;
-
-// TODO(sadovsky): Make this a nested class of Database?
-public abstract class SyncgroupInviteHandler {
- void onInvite(SyncgroupInvite invite) {
-
- }
-
- void onError(Exception e) {
-
- }
-}
diff --git a/syncbase/src/main/java/io/v/syncbase/WatchChangeHandler.java b/syncbase/src/main/java/io/v/syncbase/WatchChangeHandler.java
deleted file mode 100644
index c8d27b6..0000000
--- a/syncbase/src/main/java/io/v/syncbase/WatchChangeHandler.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package io.v.syncbase;
-
-import java.util.Iterator;
-
-// TODO(sadovsky): Make this a nested class of Database?
-public abstract class WatchChangeHandler {
- // TODO(sadovsky): Consider adopting Aaron's suggestion of combining onInitialState and
- // onChangeBatch into a single method, to make things simpler for developers who don't want to
- // apply deltas to their in-memory data structures:
- // void onChangeBatch(Iterator<WatchChange> values, Iterator<WatchChange> changes)
-
- void onInitialState(Iterator<WatchChange> values) {
-
- }
-
- void onChangeBatch(Iterator<WatchChange> changes) {
-
- }
-
- void onError(Exception e) {
-
- }
-}
diff --git a/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java b/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
index 0e71c91..123aeb3 100644
--- a/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
@@ -11,6 +11,7 @@
public void createDatabase() {
Syncbase.DatabaseOptions opts = new Syncbase.DatabaseOptions();
opts.rootDir = "/tmp";
- Syncbase.database(opts);
+ // TODO(sadovsky): Restore this once we figure out what Vanadium context to pass.
+ // Syncbase.database(opts);
}
}
\ No newline at end of file