Skip to content

Commit

Permalink
Add a MetadataUpdate class in preparation for batch object updates
Browse files Browse the repository at this point in the history
So we can pass a Map<ObjectIDResolvedWS, MetadataUpdate> to the method
  • Loading branch information
MrCreosote committed Oct 16, 2023
1 parent 6eba5e7 commit 20415a8
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 89 deletions.
3 changes: 2 additions & 1 deletion src/us/kbase/workspace/WorkspaceServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import us.kbase.typedobj.core.TypeDefId;
import us.kbase.workspace.database.DependencyStatus;
import us.kbase.workspace.database.ListObjectsParameters;
import us.kbase.workspace.database.MetadataUpdate;
import us.kbase.workspace.database.ObjectIDNoWSNoVer;
import us.kbase.workspace.database.ResourceUsageConfigurationBuilder.ResourceUsageConfiguration;
import us.kbase.workspace.database.Workspace;
Expand Down Expand Up @@ -427,7 +428,7 @@ public void alterWorkspaceMetadata(AlterWorkspaceMetadataParams params, AuthToke
}
final WorkspaceIdentifier wsi = processWorkspaceIdentifier(params.getWsi());
final WorkspaceUser user = wsmeth.getUser(authPart);
ws.setWorkspaceMetadata(user, wsi, meta, remove);
ws.setWorkspaceMetadata(user, wsi, new MetadataUpdate(meta, remove));
//END alter_workspace_metadata
}

Expand Down
77 changes: 77 additions & 0 deletions src/us/kbase/workspace/database/MetadataUpdate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package us.kbase.workspace.database;

import static us.kbase.workspace.database.Util.noNulls;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/** An update to a a set of metadata, including keys to add or replace and keys to remove. */
public class MetadataUpdate {

private final WorkspaceUserMetadata meta;
private final Set<String> remove;

/** Create the update.
* @param meta keys to add to or replace in the target metadata.
* @param toRemove keys to remove from the target metadata.
*/
public MetadataUpdate(final WorkspaceUserMetadata meta, final Collection<String> toRemove) {
this.meta = meta == null || meta.isEmpty() ? null : meta;
if (toRemove != null && !toRemove.isEmpty()) {
this.remove = Collections.unmodifiableSet(new HashSet<>(
noNulls(
toRemove,
"null metadata keys are not allowed in the remove parameter"
)
));
} else {
this.remove = null;
}
}

/** Get the keys to add or replace in the target metadata.
* @return the keys.
*/
public Optional<WorkspaceUserMetadata> getMeta() {
return Optional.ofNullable(meta);
}

/** Get the keys to remove from the target metadata.
* @return
*/
public Optional<Set<String>> getToRemove() {
return Optional.ofNullable(remove);
}

/** Return whether this metadata update has an update.
* @return true if there are keys to add, replace, or remove in this update.
*/
public boolean hasUpdate() {
return meta != null || remove != null;
}

@Override
public int hashCode() {
return Objects.hash(meta, remove);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MetadataUpdate other = (MetadataUpdate) obj;
return Objects.equals(meta, other.meta) && Objects.equals(remove, other.remove);
}

}
4 changes: 3 additions & 1 deletion src/us/kbase/workspace/database/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ public static void nonNull(final Object o, final String message) {
* @param col the collection to check.
* @param message the exception message.
* @param <T> the type of the elements in the collection.
* @return the collection.
*/
public static <T> void noNulls(final Collection<T> col, final String message) {
public static <T> Collection<T> noNulls(final Collection<T> col, final String message) {
for (final T item: col) {
if (item == null) {
throw new NullPointerException(message);
}
}
return col;
}

/** Check that the provided collection is not null and contains no null or whitespace-only
Expand Down
14 changes: 4 additions & 10 deletions src/us/kbase/workspace/database/Workspace.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,6 @@ public WorkspaceInformation createWorkspace(final WorkspaceUser user,
* @param user the user altering the metadata.
* @param wsi the workspace to alter.
* @param meta updated metadata. Keys will overwrite any keys already set on the workspace.
* Send null to make no changes.
* @param keysToRemove metadata keys to remove from the workspace. Send null to make no
* changes.
* @throws CorruptWorkspaceDBException if corrupt data is found in the database.
* @throws NoSuchWorkspaceException if the workspace does not exist or is deleted.
* @throws WorkspaceCommunicationException if a communication error occurs when contacting the
Expand All @@ -216,20 +213,17 @@ public WorkspaceInformation createWorkspace(final WorkspaceUser user,
public void setWorkspaceMetadata(
final WorkspaceUser user,
final WorkspaceIdentifier wsi,
final WorkspaceUserMetadata meta,
final List<String> keysToRemove)
final MetadataUpdate meta)
throws CorruptWorkspaceDBException, NoSuchWorkspaceException,
WorkspaceCommunicationException, WorkspaceAuthorizationException {
requireNonNull(meta, "meta");
final ResolvedWorkspaceID wsid = new PermissionsCheckerFactory(db, user)
.getWorkspaceChecker(wsi, Permission.ADMIN)
.withOperation("alter metadata for").check();
if (keysToRemove == null && (meta == null || meta.isEmpty())) {
if (!meta.hasUpdate()) {
return;
}
if (keysToRemove != null) {
noNulls(keysToRemove, "null metadata keys are not allowed");
}
final Optional<Instant> time = db.setWorkspaceMeta(wsid, meta, keysToRemove);
final Optional<Instant> time = db.setWorkspaceMeta(wsid, meta);
if (time.isPresent()) {
for (final WorkspaceEventListener l: listeners) {
l.setWorkspaceMetadata(user, wsid.getID(), time.get());
Expand Down
9 changes: 2 additions & 7 deletions src/us/kbase/workspace/database/WorkspaceDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,15 @@ WorkspaceInformation createWorkspace(WorkspaceUser owner,
* duplicate key is supplied.
*
* @param wsid the workspace for which metadata will be altered.
* @param meta the metadata to add to the workspace.
* @param remove the metadata keys to remove from the workspace. If any provided keys do
* not exist they will be ignored.
* @param meta the metadata update to apply to the workspace.
* @return the workspace modification time if the metadata was altered, or an empty optional
* if the changes had no practical effect.
* @throws WorkspaceCommunicationException if a communication error occurs.
* @throws CorruptWorkspaceDBException if the workspace database is corrupt.
* @throws IllegalArgumentException if no metadata is supplied or the
* updated metadata exceeds the allowed size.
*/
Optional<Instant> setWorkspaceMeta(
ResolvedWorkspaceID wsid,
WorkspaceUserMetadata meta,
List<String> remove)
Optional<Instant> setWorkspaceMeta(ResolvedWorkspaceID wsid, MetadataUpdate meta)
throws WorkspaceCommunicationException, CorruptWorkspaceDBException;

/** Clone a workspace.
Expand Down
31 changes: 12 additions & 19 deletions src/us/kbase/workspace/database/mongo/MongoWorkspaceDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import us.kbase.workspace.database.ByteArrayFileCacheManager;
import us.kbase.workspace.database.CopyResult;
import us.kbase.workspace.database.ListObjectsParameters.ResolvedListObjectParameters;
import us.kbase.workspace.database.MetadataUpdate;
import us.kbase.workspace.database.ObjectIDNoWSNoVer;
import us.kbase.workspace.database.ObjectIDResolvedWS;
import us.kbase.workspace.database.ObjectInfoWithModDate;
Expand Down Expand Up @@ -696,8 +697,6 @@ private void setCreatedWorkspacePermissions(
}
}

private static final Set<String> FLDS_WS_META = newHashSet(Fields.WS_META);

@FunctionalInterface
interface DocumentProvider {
Map<String, Object> getDocument()
Expand All @@ -707,34 +706,29 @@ Map<String, Object> getDocument()
@Override
public Optional<Instant> setWorkspaceMeta(
final ResolvedWorkspaceID rwsi,
final WorkspaceUserMetadata newMeta,
final List<String> remove)
final MetadataUpdate meta)
throws WorkspaceCommunicationException, CorruptWorkspaceDBException {
requireNonNull(rwsi, "rwsi");
return setMetadataOnDocument(
newMeta,
remove,
meta,
COL_WORKSPACES,
() -> query.queryWorkspace(rwsi, FLDS_WS_META),
() -> query.queryWorkspace(rwsi, newHashSet(Fields.WS_META)),
new Document(Fields.WS_ID, rwsi.getID()),
Fields.WS_META,
Fields.WS_MODDATE);
}

private Optional<Instant> setMetadataOnDocument(
final WorkspaceUserMetadata newMeta,
final List<String> remove,
final MetadataUpdate newMeta,
final String collection,
final DocumentProvider dp,
final Document identifier,
final String metaField,
final String moddateField)
throws WorkspaceCommunicationException, CorruptWorkspaceDBException {
if (remove == null && (newMeta == null || newMeta.isEmpty())) {
if (newMeta == null || !newMeta.hasUpdate()) {
throw new IllegalArgumentException("No metadata changes provided");
}
if (remove != null) {
noNulls(remove, "Null metadata keys found in remove parameter");
}
int attempts = 1;
Instant time = null;
while (time == null) {
Expand All @@ -743,11 +737,11 @@ private Optional<Instant> setMetadataOnDocument(
final List<Map<String, String>> mlist = (List<Map<String, String>>) doc.get(metaField);
final Map<String, String> oldMeta = metaMongoArrayToHash(mlist);
final Map<String, String> updatedMeta = new HashMap<>(oldMeta);
if (newMeta != null) {
updatedMeta.putAll(newMeta.getMetadata());
if (newMeta.getMeta().isPresent()) {
updatedMeta.putAll(newMeta.getMeta().get().getMetadata());
}
if (remove != null) {
for (final String k: remove) {
if (newMeta.getToRemove().isPresent()) {
for (final String k: newMeta.getToRemove().get()) {
updatedMeta.remove(k);
}
}
Expand Down Expand Up @@ -3014,8 +3008,7 @@ private Map<ObjectIDResolvedWS, ResolvedObjectID> resolveObjectIDs(
final Map<ObjectIDResolvedWS, Map<String, Object>> ids =
queryObjects(objectIDs, FLDS_RESOLVE_OBJS, exceptIfDeleted,
includeDeleted, exceptIfMissing);
final Map<ObjectIDResolvedWS, ResolvedObjectID> ret =
new HashMap<ObjectIDResolvedWS, ResolvedObjectID>();
final Map<ObjectIDResolvedWS, ResolvedObjectID> ret = new HashMap<>();
for (final ObjectIDResolvedWS o: ids.keySet()) {
final String name = (String) ids.get(o).get(Fields.OBJ_NAME);
final long id = (Long) ids.get(o).get(Fields.OBJ_ID);
Expand Down
6 changes: 4 additions & 2 deletions src/us/kbase/workspace/test/database/UtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,10 @@ public void nonNullFail() throws Exception {

@Test
public void noNullsPass() throws Exception {
Util.noNulls(NON_WHITESPACE_STRINGS, TYPE_NAME);
Util.noNulls(WHITESPACE_STRINGS, TYPE_NAME);
assertThat("incorrect return", Util.noNulls(NON_WHITESPACE_STRINGS, TYPE_NAME),
is(NON_WHITESPACE_STRINGS));
assertThat("incorrect return", Util.noNulls(WHITESPACE_STRINGS, TYPE_NAME),
is(WHITESPACE_STRINGS));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import us.kbase.workspace.database.DynamicConfig;
import us.kbase.workspace.database.ObjectIDNoWSNoVer;
import us.kbase.workspace.database.DynamicConfig.DynamicConfigUpdate;
import us.kbase.workspace.database.MetadataUpdate;
import us.kbase.workspace.database.mongo.Fields;
import us.kbase.workspace.database.mongo.GridFSBlobStore;
import us.kbase.workspace.database.mongo.MongoWorkspaceDB;
Expand Down Expand Up @@ -862,9 +863,11 @@ public void setWorkspaceMetadata() throws Exception {
// test against empty metadata
final Optional<Instant> result = mocks.mdb.setWorkspaceMeta(
rwsi,
new WorkspaceUserMetadata(ImmutableMap.of(
"foo", "bar", "baz", "bat", "to_remove", "a")),
Arrays.asList("no_key_present")
new MetadataUpdate(
new WorkspaceUserMetadata(ImmutableMap.of(
"foo", "bar", "baz", "bat", "to_remove", "a")),
Arrays.asList("no_key_present")
)
);

assertThat("incorrect time", result, is(Optional.of(inst(10000))));
Expand All @@ -877,8 +880,10 @@ public void setWorkspaceMetadata() throws Exception {
// test against non-empty metadata
final Optional<Instant> result2 = mocks.mdb.setWorkspaceMeta(
rwsi,
new WorkspaceUserMetadata(ImmutableMap.of("baz", "bing", "thingy", "thinger")),
Arrays.asList("to_remove", "somecrap")
new MetadataUpdate(
new WorkspaceUserMetadata(ImmutableMap.of("baz", "bing", "thingy", "thinger")),
Arrays.asList("to_remove", "somecrap")
)
);

assertThat("incorrect time", result2, is(Optional.of(inst(15000))));
Expand Down Expand Up @@ -910,10 +915,7 @@ public void setWorkspaceMetadataRemoveOnly() throws Exception {
when(mocks.clockmock.instant()).thenReturn(inst(10000), (Instant) null);

final Optional<Instant> result = mocks.mdb.setWorkspaceMeta(
rwsi,
null,
Arrays.asList("x", "a")
);
rwsi, new MetadataUpdate(null, Arrays.asList("x", "a")));

assertThat("incorrect time", result, is(Optional.of(inst(10000))));

Expand All @@ -937,8 +939,10 @@ public void setWorkspaceMetadataNoop() throws Exception {

final Optional<Instant> result = mocks.mdb.setWorkspaceMeta(
rwsi,
new WorkspaceUserMetadata(ImmutableMap.of("a", "b", "x", "y")),
Arrays.asList("x", "z")
new MetadataUpdate(
new WorkspaceUserMetadata(ImmutableMap.of("a", "b", "x", "y")),
Arrays.asList("x", "z")
)
);

assertThat("incorrect time", result, is(Optional.empty()));
Expand All @@ -952,13 +956,12 @@ public void setWorkspaceMetadataNoop() throws Exception {
public void setWorkspaceMetaFailBadInput() throws Exception {
final PartialMock mocks = new PartialMock(MONGO_DB);
final ResolvedWorkspaceID rwsi = new ResolvedWorkspaceID(1, "wsn", false, false);
failSetWorkspaceMeta(mocks.mdb, rwsi, null, null, new IllegalArgumentException(
"No metadata changes provided"));
final WorkspaceUserMetadata meta = new WorkspaceUserMetadata();
failSetWorkspaceMeta(mocks.mdb, rwsi, meta, null, new IllegalArgumentException(
"No metadata changes provided"));
failSetWorkspaceMeta(mocks.mdb, rwsi, null, Arrays.asList("foo", null),
new NullPointerException("Null metadata keys found in remove parameter"));
final MetadataUpdate mu = new MetadataUpdate(null, list("foo"));
failSetWorkspaceMeta(mocks.mdb, null, mu, new NullPointerException("rwsi"));
failSetWorkspaceMeta(mocks.mdb, rwsi, null,
new IllegalArgumentException("No metadata changes provided"));
failSetWorkspaceMeta(mocks.mdb, rwsi, new MetadataUpdate(null, null),
new IllegalArgumentException("No metadata changes provided"));
}

private List<Object> setWorkspaceMetaBigMetaSetup() throws Exception {
Expand Down Expand Up @@ -995,7 +998,7 @@ public void setWorkspaceMetaPassBigMeta() throws Exception {

// test against empty metadata
final Optional<Instant> result = mocks.mdb.setWorkspaceMeta(
rwsi, new WorkspaceUserMetadata(meta2), null);
rwsi, new MetadataUpdate(new WorkspaceUserMetadata(meta2), null));

assertThat("incorrect time", result, is(Optional.of(inst(10000))));

Expand All @@ -1022,19 +1025,19 @@ public void setWorkspaceMetaFailBigMeta() throws Exception {
final Map<String, String> meta2 = (Map<String, String>) setup.get(3);
meta2.put("b10", TestCommon.LONG1001.substring(0, 725));

failSetWorkspaceMeta(mocks.mdb, rwsi, new WorkspaceUserMetadata(meta2), null,
failSetWorkspaceMeta(
mocks.mdb, rwsi, new MetadataUpdate(new WorkspaceUserMetadata(meta2), null),
new IllegalArgumentException("Updated metadata exceeds allowed size of 16000B"));
}

private void failSetWorkspaceMeta(
final MongoWorkspaceDB mdb,
final ResolvedWorkspaceID rwsi,
final WorkspaceUserMetadata meta,
final List<String> remove,
final MetadataUpdate meta,
final Exception expected)
throws Exception {
try {
mdb.setWorkspaceMeta(rwsi, meta, remove);
mdb.setWorkspaceMeta(rwsi, meta);
fail("expected exception");
} catch (Exception got) {
TestCommon.assertExceptionCorrect(got, expected);
Expand Down
Loading

0 comments on commit 20415a8

Please sign in to comment.