Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a MetadataUpdate class in preparation for batch object updates #697

Merged
merged 1 commit into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much nicer!

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