From 536c0e4953c36c5c2329e203587e99939e6411ed Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 12:11:35 -0700 Subject: [PATCH 01/39] Part 1 of fixing private admin'd resources bug Expose resources the user administrates in private groups. However, this also exposes public resources in private groups, which will be fixed next. --- README.md | 3 +- RELEASE_NOTES.md | 5 ++++ src/us/kbase/groups/core/GroupView.java | 4 ++- .../kbase/groups/service/api/APICommon.java | 12 ++++---- src/us/kbase/groups/service/api/Root.java | 2 +- .../kbase/test/groups/core/GroupViewTest.java | 13 ++++++++- .../integration/ServiceIntegrationTest.java | 7 ++++- .../groups/service/api/APICommonTest.java | 29 +++++++++++++++++++ .../groups/service/api/GroupsAPITest.java | 1 + .../test/groups/service/api/RootTest.java | 2 +- 10 files changed, 67 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b7f339a3..e543fd4f 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,8 @@ RETURNS: A Group. ``` If the user is not a member of the group or no authorization is provided and the group is -private, only the `groupid`, `private`, and `role` fields are included. +private, only the `groupid`, `private`, `role`, and `resources` fields are included. Only +resources the user user administrates will be available in `resources`. If no authorization is provided, the members list is populated or not based on the `privatemembers` field, only public custom fields are included, and only public resources diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 613fbcad..c6602dcf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,10 @@ # KBase Groups Service release notes +## 0.1.6 + +* Fixed a bug where resource administrators could not see their resources in private groups + for which they were not a member in the `/group/ endpoint`. + ## 0.1.5 * Group administrators may now promote and demote other administrators. diff --git a/src/us/kbase/groups/core/GroupView.java b/src/us/kbase/groups/core/GroupView.java index f739f188..a08056ca 100644 --- a/src/us/kbase/groups/core/GroupView.java +++ b/src/us/kbase/groups/core/GroupView.java @@ -185,7 +185,8 @@ private GroupView( this.isPrivate = group.isPrivate(); this.groupID = group.getGroupID(); if (isPrivateView()) { - this.resourceInfo = Collections.emptyMap(); + // resInfo is expected to only contain admin'd resources for non-members + this.resourceInfo = Collections.unmodifiableMap(resourceInfo); this.resourceJoinDate = Collections.emptyMap(); this.groupName = Optional.empty(); this.owner = Optional.empty(); @@ -659,6 +660,7 @@ public Builder withStandardView(final boolean isStandardView) { /** Add resource information to the view. The resource type and the resource IDs for * that type must exist in the group, and the information set may not include * nonexistent resources. + * The resource information will be visible to any user able to access this object. * Calling this method will overwrite any previous information for the type. * @param type the type of the resource. * @param info the information for the resource. diff --git a/src/us/kbase/groups/service/api/APICommon.java b/src/us/kbase/groups/service/api/APICommon.java index 92854ce8..03353519 100644 --- a/src/us/kbase/groups/service/api/APICommon.java +++ b/src/us/kbase/groups/service/api/APICommon.java @@ -80,6 +80,13 @@ public static Map toGroupJSON(final GroupView group) { ret.put(Fields.GROUP_ID, group.getGroupID().getName()); ret.put(Fields.GROUP_IS_PRIVATE, group.isPrivate()); ret.put(Fields.GROUP_ROLE, group.getRole().getRepresentation()); + if (group.isStandardView()) { + final Map resources = new HashMap<>(); + ret.put(Fields.GROUP_RESOURCES, resources); + for (final ResourceType t: group.getResourceTypes()) { + resources.put(t.getName(), getResourceList(group, t)); + } + } if (!group.isPrivateView()) { ret.put(Fields.GROUP_NAME, group.getGroupName().get().getName()); ret.put(Fields.GROUP_OWNER, group.getOwner().get().getName()); @@ -98,11 +105,6 @@ public static Map toGroupJSON(final GroupView group) { ret.put(Fields.GROUP_OWNER, toUserJson(group.getMember(group.getOwner().get()))); ret.put(Fields.GROUP_MEMBERS, toMemberList(group.getMembers(), group)); ret.put(Fields.GROUP_ADMINS, toMemberList(group.getAdministrators(), group)); - final Map resources = new HashMap<>(); - ret.put(Fields.GROUP_RESOURCES, resources); - for (final ResourceType t: group.getResourceTypes()) { - resources.put(t.getName(), getResourceList(group, t)); - } } } return ret; diff --git a/src/us/kbase/groups/service/api/Root.java b/src/us/kbase/groups/service/api/Root.java index 89882677..12dc02c3 100644 --- a/src/us/kbase/groups/service/api/Root.java +++ b/src/us/kbase/groups/service/api/Root.java @@ -26,7 +26,7 @@ public class Root { //TODO ZLATER ROOT add configurable contact email or link //TODO ZLATER swagger - private static final String VERSION = "0.1.5"; + private static final String VERSION = "0.1.6-dev1"; private static final String SERVER_NAME = "Groups service"; /** Return the root information. diff --git a/src/us/kbase/test/groups/core/GroupViewTest.java b/src/us/kbase/test/groups/core/GroupViewTest.java index 81f3286d..760a66b2 100644 --- a/src/us/kbase/test/groups/core/GroupViewTest.java +++ b/src/us/kbase/test/groups/core/GroupViewTest.java @@ -418,7 +418,18 @@ public void privateNonMemberView() throws Exception { assertThat("incorrect own", gv.getOwner(), is(mt())); assertThat("incorrect view type", gv.isStandardView(), is(true)); assertThat("incorrect role", gv.getRole(), is(Group.Role.NONE)); - assertThat("incorrect types", gv.getResourceTypes(), is(set())); + assertThat("incorrect types", gv.getResourceTypes(), is(set(new ResourceType("bar"), + new ResourceType("workspace"), new ResourceType("catalogmethod")))); + assertThat("incorrect info", gv.getResourceInformation(new ResourceType("bar")), + is(ResourceInformationSet.getBuilder(null).build())); + assertThat("incorrect info", gv.getResourceInformation(new ResourceType("workspace")), + is(ResourceInformationSet.getBuilder(null) + .withResource(new ResourceID("7")) + .build())); + assertThat("incorrect info", gv.getResourceInformation(new ResourceType("catalogmethod")), + is(ResourceInformationSet.getBuilder(null) + .build())); + assertThat("incorrect custom", gv.getCustomFields(), is(Collections.emptyMap())); getMemberFail(gv, new UserName("user")); diff --git a/src/us/kbase/test/groups/integration/ServiceIntegrationTest.java b/src/us/kbase/test/groups/integration/ServiceIntegrationTest.java index e5c0844c..3837f0d6 100644 --- a/src/us/kbase/test/groups/integration/ServiceIntegrationTest.java +++ b/src/us/kbase/test/groups/integration/ServiceIntegrationTest.java @@ -411,7 +411,12 @@ private void assertGroupIsPrivate(final String groupID, final String token) { final Map g = res.readEntity(Map.class); assertThat("group is not private", g, is(ImmutableMap.of( - "id", groupID, "private", true, "role", "None"))); + "id", groupID, + "private", true, + "role", "None", + "resources", ImmutableMap.of( + "catalogmethod", Collections.emptyList(), + "workspace", Collections.emptyList())))); } private void assertGroupExists(final String gid, boolean exists) { diff --git a/src/us/kbase/test/groups/service/api/APICommonTest.java b/src/us/kbase/test/groups/service/api/APICommonTest.java index 99929c48..a60d0fe0 100644 --- a/src/us/kbase/test/groups/service/api/APICommonTest.java +++ b/src/us/kbase/test/groups/service/api/APICommonTest.java @@ -513,6 +513,35 @@ public void toGroupJSONStandardViewResourcesNonMember() throws Exception { .build())); } + @Test + public void toGroupJSONStandardViewResourcesNonMemberPrivate() throws Exception { + final UserName userName = new UserName("non"); + final GroupView gv = GroupView.getBuilder( + getGroupMaxBuilder() + .withIsPrivate(true) + .build(), + userName) + .withResource(new ResourceType("ws"), ResourceInformationSet + .getBuilder(userName) + .withResourceField(new ResourceID("a"), "f1", "x") + .build()) + .withStandardView(true) + .build(); + + assertThat("incorrect JSON", APICommon.toGroupJSON(gv), is(MapBuilder.newHashMap() + .with("id", "id2") + .with("private", true) + .with("role", "None") + .with("resources", ImmutableMap.of( + "ws", Arrays.asList( + MapBuilder.newHashMap() + .with("rid", "a") + .with("added", null) + .with("f1", "x") + .build()))) + .build())); + } + @Test public void toGroupJSONFail() throws Exception { try { diff --git a/src/us/kbase/test/groups/service/api/GroupsAPITest.java b/src/us/kbase/test/groups/service/api/GroupsAPITest.java index d2c96874..d951bb7c 100644 --- a/src/us/kbase/test/groups/service/api/GroupsAPITest.java +++ b/src/us/kbase/test/groups/service/api/GroupsAPITest.java @@ -262,6 +262,7 @@ private static Builder getGroupMaxBuilder() .with("id", "id2") .with("private", true) .with("role", "None") + .with("resources", Collections.emptyMap()) .build(); @Test diff --git a/src/us/kbase/test/groups/service/api/RootTest.java b/src/us/kbase/test/groups/service/api/RootTest.java index 3a5305fe..f0acda92 100644 --- a/src/us/kbase/test/groups/service/api/RootTest.java +++ b/src/us/kbase/test/groups/service/api/RootTest.java @@ -17,7 +17,7 @@ public class RootTest { - public static final String SERVER_VER = "0.1.5"; + public static final String SERVER_VER = "0.1.6-dev1"; private static final String GIT_ERR = "Missing git commit file gitcommit, should be in us.kbase.groups"; From 91fc357e6e0a664843d073d4ac52216e9eb278b0 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 15:27:17 -0700 Subject: [PATCH 02/39] Prevent public resources from showing up in private groups to non members who aren't administrators of the resource. --- .../SDKClientCatalogHandler.java | 44 +++++++++++-- src/us/kbase/groups/core/Groups.java | 13 +++- .../groups/core/resource/ResourceAccess.java | 18 +++++ .../groups/core/resource/ResourceHandler.java | 6 +- .../SDKClientWorkspaceHandler.java | 28 ++++++-- .../SDKClientCatalogHandlerTest.java | 63 +++++++++++++++--- src/us/kbase/test/groups/core/GroupsTest.java | 64 ++++++++++++++++-- .../SDKClientWorkspaceHandlerTest.java | 66 +++++++++++++------ 8 files changed, 248 insertions(+), 54 deletions(-) create mode 100644 src/us/kbase/groups/core/resource/ResourceAccess.java diff --git a/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java b/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java index 2e156723..08612377 100644 --- a/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java +++ b/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java @@ -1,14 +1,17 @@ package us.kbase.groups.cataloghandler; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import static us.kbase.groups.util.Util.checkNoNullsInCollection; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import us.kbase.catalog.BasicModuleInfo; import us.kbase.catalog.CatalogClient; @@ -22,6 +25,7 @@ import us.kbase.groups.core.exceptions.MissingParameterException; import us.kbase.groups.core.exceptions.NoSuchResourceException; import us.kbase.groups.core.exceptions.ResourceHandlerException; +import us.kbase.groups.core.resource.ResourceAccess; import us.kbase.groups.core.resource.ResourceAdministrativeID; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceHandler; @@ -191,19 +195,45 @@ public ResourceDescriptor getDescriptor(final ResourceID resource) @Override public ResourceInformationSet getResourceInformation( final UserName user, - final Set resources, - final boolean administratedResourcesOnly) - throws IllegalResourceIDException { + Set resources, + final ResourceAccess access) + throws IllegalResourceIDException, ResourceHandlerException { checkNoNullsInCollection(resources, "resources"); - final Builder b = ResourceInformationSet.getBuilder(user); - + requireNonNull(access, "access"); for (final ResourceID r: resources) { - getModMeth(r); // check id is valid - b.withResource(r); + getModMeth(r); // check ids are valid before we do anything else } + // check != so that if another level is added to ResourceAccess it doesn't + // accidentally grant access + if (!ResourceAccess.ALL.equals(access) && + !ResourceAccess.ADMINISTRATED_AND_PUBLIC.equals(access)) { + if (user == null) { + resources = Collections.emptySet(); + } else { + resources = filterNonAdministrated(resources, user); + } + } + final Builder b = ResourceInformationSet.getBuilder(user); + resources.stream().forEach(r -> b.withResource(r)); return b.build(); } + private Set filterNonAdministrated( + final Set resources, + final UserName user) + throws ResourceHandlerException, IllegalResourceIDException { + // there are no bulk methods for getting catalog methods, so just list all admin'd mods + final Set admined = getAdministratedResources(user).stream().map(r -> r.getName()) + .collect(Collectors.toSet()); + final Set ret = new HashSet<>(); + for (final ResourceID r: resources) { + if (admined.contains(getModMeth(r).mod)) { + ret.add(r); + } + } + return ret; + } + @Override public void setReadPermission(ResourceID resource, UserName user) { return; // nothing to do, catalog methods are all public diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index 49001dba..398005b9 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -50,6 +50,7 @@ import us.kbase.groups.core.request.GroupRequestWithActions; import us.kbase.groups.core.request.RequestID; import us.kbase.groups.core.request.RequestType; +import us.kbase.groups.core.resource.ResourceAccess; import us.kbase.groups.core.resource.ResourceAdministrativeID; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceHandler; @@ -402,7 +403,7 @@ private ResourceInformationSet getResourceInfo( user, g.getResources(type).stream().map(r -> r.getResourceID()) .collect(Collectors.toSet()), - !g.isMember(user)); + getAccessLevel(g, user)); } catch (IllegalResourceIDException e) { throw new RuntimeException(String.format( "Illegal data associated with group %s: %s", @@ -418,6 +419,16 @@ private ResourceInformationSet getResourceInfo( return info; } + private ResourceAccess getAccessLevel(final Group g, final UserName user) { + if (g.isMember(user)) { + return ResourceAccess.ALL; + } else if (!g.isPrivate()) { + return ResourceAccess.ADMINISTRATED_AND_PUBLIC; + } else { + return ResourceAccess.ADMINISTRATED; + } + } + /** Check if a group exists based on the group ID. * @param groupID the group ID. * @return true if the group exists, false otherwise. diff --git a/src/us/kbase/groups/core/resource/ResourceAccess.java b/src/us/kbase/groups/core/resource/ResourceAccess.java new file mode 100644 index 00000000..93abe9b2 --- /dev/null +++ b/src/us/kbase/groups/core/resource/ResourceAccess.java @@ -0,0 +1,18 @@ +package us.kbase.groups.core.resource; + +/** An enum designating what resources should be made available to a user. + * @author gaprice@lbl.gov + * + */ +public enum ResourceAccess { + + /** All resources. */ + ALL, + + /** Resources administrated by the user and public resources. */ + ADMINISTRATED_AND_PUBLIC, + + /** Resources administrated by the user. */ + ADMINISTRATED; + +} diff --git a/src/us/kbase/groups/core/resource/ResourceHandler.java b/src/us/kbase/groups/core/resource/ResourceHandler.java index 3edc03ce..5df40d27 100644 --- a/src/us/kbase/groups/core/resource/ResourceHandler.java +++ b/src/us/kbase/groups/core/resource/ResourceHandler.java @@ -59,8 +59,8 @@ Set getAdministratedResources(UserName user) * @param user the user at which any user-specific fields should be targeted. If null, * indicating an anonymous user, only public resources are returned. * @param resources the resources to retrieve. - * @param administratedResourcesOnly if true, only return public resources and resources - * the user administrates. If false, return all resources (unless the user is null). + * @param access the level of access to the resources the user possesses. Note that + * an anonymous user will, in most cases, administrate no resources. * @return the resource information. * @throws IllegalResourceIDException if the resource ID is not in a legal format. * @throws ResourceHandlerException if an error occurs contacting the resource service. @@ -68,7 +68,7 @@ Set getAdministratedResources(UserName user) ResourceInformationSet getResourceInformation( UserName user, Set resources, - boolean administratedResourcesOnly) + ResourceAccess access) throws IllegalResourceIDException, ResourceHandlerException; /** Grant a user permission to view or read a resource, if the user does not already have diff --git a/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java b/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java index 89b20daa..476bbbf4 100644 --- a/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java +++ b/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java @@ -1,6 +1,7 @@ package us.kbase.groups.workspacehandler; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import static us.kbase.groups.util.Util.checkNoNullsInCollection; import java.io.IOException; @@ -30,6 +31,7 @@ import us.kbase.groups.core.exceptions.MissingParameterException; import us.kbase.groups.core.exceptions.NoSuchResourceException; import us.kbase.groups.core.exceptions.ResourceHandlerException; +import us.kbase.groups.core.resource.ResourceAccess; import us.kbase.groups.core.resource.ResourceAdministrativeID; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceHandler; @@ -191,12 +193,10 @@ private Integer getWorkspaceID(final ServerException e) { public ResourceInformationSet getResourceInformation( final UserName user, final Set resources, - boolean administratedResourcesOnly) + final ResourceAccess access) throws ResourceHandlerException, IllegalResourceIDException { checkNoNullsInCollection(resources, "resources"); - if (user == null) { - administratedResourcesOnly = true; // only return public workspaces - } + requireNonNull(access, "access"); //TODO WS make a bulk ws method for getwsinfo that returns error code (DELETED, MISSING, INACCESSIBLE, etc.) for inaccessible workspaces //TODO WS for get perms mass make ignore error option that returns error state (DELETED, MISSING, INACCESSIBLE etc.) and use here instead of going one at a time final ResourceInformationSet.Builder b = ResourceInformationSet.getBuilder(user); @@ -212,7 +212,7 @@ public ResourceInformationSet getResourceInformation( b.withNonexistentResource(rid); } else { final Perm perm = new Perm(user, perms.perms.get(0)); - if (!administratedResourcesOnly || perm.perm.isAdmin() || perm.isPublic) { + if (hasAccess(perm, access)) { final WSInfoOwner wi = getWSInfo(wsid); if (wi == null) { // should almost never happen since we checked for inaccessible ws above @@ -232,6 +232,20 @@ public ResourceInformationSet getResourceInformation( return b.build(); } + // hm. This seems nasty, but the ResourceAccess class makes sense to me... + // For now I'll keep the nasty implementation and more readable, IMO, API. + private boolean hasAccess(final Perm perm, final ResourceAccess access) { + if (ResourceAccess.ALL.equals(access)) { + return true; + } else if (ResourceAccess.ADMINISTRATED_AND_PUBLIC.equals(access) && + (perm.perm.isAdmin() || perm.isPublic)) { + return true; + } else if (ResourceAccess.ADMINISTRATED.equals(access) && perm.perm.isAdmin()) { + return true; + } + return false; + } + private static final TypeReference>> WS_INFO_TYPEREF = new TypeReference meta) + private NarrInfo getNarrativeInfo(final long wsid, final Map meta) throws IOException, JsonClientException { if ("false".equals(meta.get("is_temporary")) && meta.containsKey("narrative")) { final String name = meta.get("narrative_nice_name"); diff --git a/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java b/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java index 1b8e3973..826d6752 100644 --- a/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java +++ b/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java @@ -36,6 +36,7 @@ import us.kbase.groups.core.exceptions.ResourceHandlerException; import us.kbase.groups.core.exceptions.IllegalResourceIDException; import us.kbase.groups.core.exceptions.NoSuchResourceException; +import us.kbase.groups.core.resource.ResourceAccess; import us.kbase.groups.core.resource.ResourceAdministrativeID; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceID; @@ -546,23 +547,32 @@ private void failGetDescriptor(final ResourceID r, final Exception expected) { } @Test - public void getResourceInformationMinimal() throws Exception { + public void getResourceInformationNoResources() throws Exception { final CatalogClient c = mock(CatalogClient.class); final ResourceInformationSet ris = new SDKClientCatalogHandler(c) - .getResourceInformation(null, set(), true); + .getResourceInformation(null, set(), ResourceAccess.ADMINISTRATED_AND_PUBLIC); assertThat("incorrect infos", ris, is(ResourceInformationSet.getBuilder(null).build())); } @Test - public void getResourceInformationMaximal() throws Exception { + public void getResourceInformationAllAccess() throws Exception { + getResourceInformation(ResourceAccess.ALL); + } + + @Test + public void getResourceInformationAdminAndPublicAccess() throws Exception { + getResourceInformation(ResourceAccess.ADMINISTRATED_AND_PUBLIC); + } + + private void getResourceInformation(final ResourceAccess access) throws Exception { final CatalogClient c = mock(CatalogClient.class); final ResourceInformationSet ris = new SDKClientCatalogHandler(c) .getResourceInformation(new UserName("foo"), set(new ResourceID("foo.bar"), new ResourceID("x.y")), - false); + access); assertThat("incorrect infos", ris, is(ResourceInformationSet.getBuilder( new UserName("foo")) @@ -571,14 +581,50 @@ public void getResourceInformationMaximal() throws Exception { .build())); } + @Test + public void getResourceInformationAdminAccessNullUser() throws Exception { + final CatalogClient c = mock(CatalogClient.class); + + final ResourceInformationSet ris = new SDKClientCatalogHandler(c) + .getResourceInformation(null, + set(new ResourceID("foo.bar"), new ResourceID("x.y")), + ResourceAccess.ADMINISTRATED); + + assertThat("incorrect infos", ris, is(ResourceInformationSet.getBuilder(null).build())); + } + + @Test + public void getResourceInformationAdminAccess() throws Exception { + // don't retest the code from getAdministratedResources + final CatalogClient c = mock(CatalogClient.class); + + when(c.listBasicModuleInfo(argThat(new ListModuleParamsMatcher("foo", 1)))) + .thenReturn(Arrays.asList( + new BasicModuleInfo().withModuleName("m1"), + new BasicModuleInfo().withModuleName("m2"), + new BasicModuleInfo().withModuleName("m3"))); + + final ResourceInformationSet ris = new SDKClientCatalogHandler(c) + .getResourceInformation(new UserName("foo"), + set(new ResourceID("m2.method"), new ResourceID("m4.method")), + ResourceAccess.ADMINISTRATED); + + assertThat("incorrect infos", ris, is(ResourceInformationSet.getBuilder( + new UserName("foo")) + .withResource(new ResourceID("m2.method")) + .build())); + } + @Test public void getResourceInformationFailBadArgs() throws Exception { - failGetResourceInformation(null, new NullPointerException("resources")); - failGetResourceInformation(set(new ResourceID("i"), null), + final ResourceAccess a = ResourceAccess.ALL; + failGetResourceInformation(null, a, new NullPointerException("resources")); + failGetResourceInformation(set(new ResourceID("i"), null), a, new NullPointerException("Null item in collection resources")); + failGetResourceInformation(set(), null, new NullPointerException("access")); for (final String n: BAD_NAMES.keySet()) { - failGetResourceInformation(set(new ResourceID("x.y"), new ResourceID(n)), + failGetResourceInformation(set(new ResourceID("x.y"), new ResourceID(n)), a, new IllegalResourceIDException("Illegal catalog method name: " + BAD_NAMES.get(n))); } @@ -586,10 +632,11 @@ public void getResourceInformationFailBadArgs() throws Exception { private void failGetResourceInformation( final Set resources, + final ResourceAccess access, final Exception expected) { final CatalogClient c = mock(CatalogClient.class); try { - new SDKClientCatalogHandler(c).getResourceInformation(null, resources, true); + new SDKClientCatalogHandler(c).getResourceInformation(null, resources, access); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index f2d947de..125d99d7 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -83,6 +83,7 @@ import us.kbase.groups.core.request.GroupRequestWithActions; import us.kbase.groups.core.request.RequestID; import us.kbase.groups.core.request.RequestType; +import us.kbase.groups.core.resource.ResourceAccess; import us.kbase.groups.core.resource.ResourceAdministrativeID; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceHandler; @@ -886,8 +887,8 @@ public void getGroupNoToken() throws Exception { when(mocks.validators.getUserFieldConfigOrEmpty(new CustomField("noconfig"))) .thenReturn(Optional.empty()); - when(mocks.wsHandler.getResourceInformation( - null, set(new ResourceID("92"), new ResourceID("86")), true)) + when(mocks.wsHandler.getResourceInformation(null, set(new ResourceID("92"), + new ResourceID("86")), ResourceAccess.ADMINISTRATED_AND_PUBLIC)) .thenReturn(ResourceInformationSet.getBuilder(null) .withResourceField(new ResourceID("92"), "name", "my ws") .withResourceField(new ResourceID("92"), "public", true) @@ -924,7 +925,7 @@ null, set(new ResourceID("92"), new ResourceID("86")), true)) } @Test - public void getGroupNonMemberToken() throws Exception { + public void getGroupNonMember() throws Exception { // can get public and admin'd resources // tests that there's still a resource entry in the group view if missing from the group final TestMocks mocks = initTestMocks(); @@ -961,7 +962,7 @@ public void getGroupNonMemberToken() throws Exception { when(mocks.wsHandler.getResourceInformation( new UserName("whee"), set(new ResourceID("92"), new ResourceID("57"), - new ResourceID("86")), true)) + new ResourceID("86")), ResourceAccess.ADMINISTRATED_AND_PUBLIC)) .thenReturn(ResourceInformationSet.getBuilder(new UserName("whee")) .withResourceField(new ResourceID("92"), "name", "my ws") .withResourceField(new ResourceID("57"), "name", "my ws2") @@ -1016,6 +1017,54 @@ public void getGroupNonMemberToken() throws Exception { .build())); } + @Test + public void getGroupNonMemberPrivate() throws Exception { + // can get admin'd resources + final TestMocks mocks = initTestMocks(); + + when(mocks.storage.getGroup(new GroupID("bar"))).thenReturn(Group.getBuilder( + new GroupID("bar"), new GroupName("name"), + GroupUser.getBuilder(new UserName("foo"), inst(10000)).build(), + new CreateAndModTimes(Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withIsPrivate(true) + .withMember(GroupUser.getBuilder(new UserName("baz"), inst(20000)).build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("92"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("57"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("86"))) + .build()); + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("whee")); + + when(mocks.wsHandler.getResourceInformation( + new UserName("whee"), set(new ResourceID("92"), new ResourceID("57"), + new ResourceID("86")), ResourceAccess.ADMINISTRATED)) + .thenReturn(ResourceInformationSet.getBuilder(new UserName("whee")) + .withResourceField(new ResourceID("92"), "name", "my ws") + .build()); + + final GroupView g = mocks.groups.getGroup(new Token("token"), new GroupID("bar")); + + assertThat("incorrect group", g, is(GroupView.getBuilder(Group.getBuilder( + new GroupID("bar"), new GroupName("name"), + GroupUser.getBuilder(new UserName("foo"), inst(10000)).build(), + new CreateAndModTimes(Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withIsPrivate(true) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("92"))) + .build(), new UserName("whee")) + .withStandardView(true) + .withPublicFieldDeterminer(f -> true) + .withPublicUserFieldDeterminer(f -> true) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceType("workspace"), ResourceInformationSet + .getBuilder(new UserName("whee")) + .withResourceField(new ResourceID("92"), "name", "my ws") + .build()) + .build())); + } + @Test public void getGroupMemberTokenWithNonexistentResources() throws Exception { // tests non existent resource code @@ -1064,7 +1113,8 @@ public void getGroupMemberTokenWithNonexistentResources() throws Exception { when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("baz")); when(mocks.wsHandler.getResourceInformation( new UserName("baz"), set(new ResourceID("92"), new ResourceID("6"), - new ResourceID("57"), new ResourceID("86"), new ResourceID("34")), false)) + new ResourceID("57"), new ResourceID("86"), new ResourceID("34")), + ResourceAccess.ALL)) .thenReturn(ResourceInformationSet.getBuilder(new UserName("baz")) .withResourceField(new ResourceID("92"), "name", "my ws") .withResourceField(new ResourceID("6"), "name", "my other ws") @@ -1075,7 +1125,7 @@ public void getGroupMemberTokenWithNonexistentResources() throws Exception { .build()); when(mocks.catHandler.getResourceInformation( new UserName("baz"), set(new ResourceID("mod1.meth1"), - new ResourceID("mod2.meth2")), false)) + new ResourceID("mod2.meth2")), ResourceAccess.ALL)) .thenReturn(ResourceInformationSet.getBuilder(new UserName("baz")) .withResource(new ResourceID("mod2.meth2")) .build()); @@ -1196,7 +1246,7 @@ public void getGroupFailIllegalResource() throws Exception { .build()); when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("baz")); when(mocks.wsHandler.getResourceInformation( - new UserName("baz"), set(new ResourceID("not a ws id")), false)) + new UserName("baz"), set(new ResourceID("not a ws id")), ResourceAccess.ALL)) .thenThrow(new IllegalResourceIDException("oh heck")); failGetGroup(mocks.groups, new Token("token"), new GroupID("bar"), new RuntimeException( diff --git a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java index c6fef4b2..74fdc5ac 100644 --- a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java +++ b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java @@ -33,6 +33,7 @@ import us.kbase.groups.core.exceptions.IllegalResourceIDException; import us.kbase.groups.core.exceptions.NoSuchResourceException; import us.kbase.groups.core.exceptions.ResourceHandlerException; +import us.kbase.groups.core.resource.ResourceAccess; import us.kbase.groups.core.resource.ResourceAdministrativeID; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceID; @@ -284,7 +285,7 @@ public void getResourceInformationNoWS() throws Exception { final SDKClientWorkspaceHandler h = new SDKClientWorkspaceHandler(c); final ResourceInformationSet wi = h.getResourceInformation( - new UserName("u"), set(), false); + new UserName("u"), set(), ResourceAccess.ALL); assertThat("incorrect wsi", wi, is(ResourceInformationSet.getBuilder(new UserName("u")) .build())); @@ -294,7 +295,7 @@ public void getResourceInformationNoWS() throws Exception { public void getResourceInformationFull() throws Exception { getResourceInformation( new UserName("user1"), - false, + ResourceAccess.ALL, ResourceInformationSet.getBuilder(new UserName("user1")) .withNonexistentResource(RD8) .withNonexistentResource(RD9) @@ -343,10 +344,10 @@ public void getResourceInformationFull() throws Exception { } @Test - public void getResourceInformationAdministratedOnly() throws Exception { + public void getResourceInformationAdminPublic() throws Exception { getResourceInformation( new UserName("user1"), - true, + ResourceAccess.ADMINISTRATED_AND_PUBLIC, ResourceInformationSet.getBuilder(new UserName("user1")) .withNonexistentResource(RD9) .withNonexistentResource(RD20) @@ -387,20 +388,40 @@ public void getResourceInformationAdministratedOnly() throws Exception { } @Test - public void getResourceInformationAnonUser() throws Exception { - getResourceInformationAnonUser(false); + public void getResourceInformationAdmin() throws Exception { + getResourceInformation( + new UserName("user1"), + ResourceAccess.ADMINISTRATED, + ResourceInformationSet.getBuilder(new UserName("user1")) + .withNonexistentResource(RD20) + .withNonexistentResource(RD21) + .withResourceField(RD3, "name", "name3") + .withResourceField(RD3, "public", false) + .withResourceField(RD3, "narrname", null) + .withResourceField(RD3, "narrcreate", null) + .withResourceField(RD3, "perm", "Own") + .withResourceField(RD3, "description", "my desc") + .withResourceField(RD3, "moddate", 1540606613000L) + .withResourceField(RD5, "name", "name5") + .withResourceField(RD5, "public", false) + .withResourceField(RD5, "narrname", "narr_name") + .withResourceField(RD5, "narrcreate", 1500000000000L) + .withResourceField(RD5, "perm", "Admin") + .withResourceField(RD5, "description", null) + .withResourceField(RD5, "moddate", 0L) + .build()); } @Test - public void getResourceInformationAnonUserAdministratedWSOnly() throws Exception { - getResourceInformationAnonUser(true); + public void getResourceInformationAnonUser() throws Exception { + getResourceInformationAnonUser(ResourceAccess.ADMINISTRATED_AND_PUBLIC); } - - private void getResourceInformationAnonUser(final boolean administratedWorkspacesOnly) + + private void getResourceInformationAnonUser(final ResourceAccess access) throws Exception { getResourceInformation( null, - administratedWorkspacesOnly, + access, ResourceInformationSet.getBuilder(null) .withNonexistentResource(RD9) .withNonexistentResource(RD20) @@ -428,7 +449,7 @@ private void getResourceInformationAnonUser(final boolean administratedWorkspace private void getResourceInformation( final UserName user, - final boolean administratedResourcesOnly, + final ResourceAccess access, final ResourceInformationSet expected) throws Exception { final WorkspaceClient c = mock(WorkspaceClient.class); @@ -566,7 +587,7 @@ private void getResourceInformation( new ResourceID("11"), new ResourceID("20"), new ResourceID("21"), new ResourceID("30"), new ResourceID("31"), new ResourceID("40"), new ResourceID("41")), - administratedResourcesOnly); + access); assertThat("incorrect resources", ri, is(expected)); } @@ -630,11 +651,13 @@ public void getResourceInformationFailBadArgs() throws Exception { when(c.ver()).thenReturn(MIN_WS_VER); final SDKClientWorkspaceHandler h = new SDKClientWorkspaceHandler(c); + final ResourceAccess a = ResourceAccess.ALL; - failGetResourceInfo(h, null, new NullPointerException("resources")); - failGetResourceInfo(h, set(new ResourceID("1"), null), + failGetResourceInfo(h, null, a, new NullPointerException("resources")); + failGetResourceInfo(h, set(new ResourceID("1"), null), a, new NullPointerException("Null item in collection resources")); - failGetResourceInfo(h, set(new ResourceID("bar")), + failGetResourceInfo(h, set(), null, new NullPointerException("access")); + failGetResourceInfo(h, set(new ResourceID("bar")), a, new IllegalResourceIDException("bar")); } @@ -679,7 +702,7 @@ private void failGetResourceInfoOnPermissionsCall( when(c.administer(argThat(getPermissionsCommandMatcher(24)))).thenThrow(thrown); - failGetResourceInfo(h, set(new ResourceID("24")), expected); + failGetResourceInfo(h, set(new ResourceID("24")), ResourceAccess.ALL, expected); } @Test @@ -727,7 +750,7 @@ private void failGetResourceInfoOnGetWSCall( doThrow(thrown).when(c).administer(argThat(getWSInfoCommandMatcher(24))); - failGetResourceInfo(h, set(new ResourceID("24")), expected); + failGetResourceInfo(h, set(new ResourceID("24")), ResourceAccess.ALL, expected); } @Test @@ -779,7 +802,7 @@ private void failGetResourceInfoOnGetDescriptionCall( doThrow(thrown).when(c).administer(argThat(getWSDescCommandMatcher(24))); - failGetResourceInfo(h, set(new ResourceID("24")), expected); + failGetResourceInfo(h, set(new ResourceID("24")), ResourceAccess.ALL, expected); } @Test @@ -852,16 +875,17 @@ private void failGetResourceInfoOnGetObjectInfoCall( doThrow(thrown).when(c).administer(argThat(getObjectInfoCommandMatcher(24, 4, 1))); - failGetResourceInfo(h, set(new ResourceID("24")), expected); + failGetResourceInfo(h, set(new ResourceID("24")), ResourceAccess.ALL, expected); } private void failGetResourceInfo( final SDKClientWorkspaceHandler h, final Set ids, + final ResourceAccess access, final Exception expected) { try { // no way to cause a fail via user or adminOnly param - h.getResourceInformation(null, ids, false); + h.getResourceInformation(null, ids, access); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); From 92daa44b22fd636c74673d85237708f31d21402f Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 15:43:38 -0700 Subject: [PATCH 03/39] Improve WS handler anon user tests --- .../SDKClientWorkspaceHandlerTest.java | 112 +++++++++++++----- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java index 74fdc5ac..ac663c6d 100644 --- a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java +++ b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java @@ -343,6 +343,58 @@ public void getResourceInformationFull() throws Exception { .build()); } + @Test + public void getResourceInformationFullAnonUser() throws Exception { + getResourceInformation( + null, + ResourceAccess.ALL, + ResourceInformationSet.getBuilder(null) + .withNonexistentResource(RD8) + .withNonexistentResource(RD9) + .withNonexistentResource(RD20) + .withNonexistentResource(RD21) + .withNonexistentResource(RD30) + .withNonexistentResource(RD31) + .withNonexistentResource(RD40) + .withNonexistentResource(RD41) + .withResourceField(RD3, "name", "name3") + .withResourceField(RD3, "public", false) + .withResourceField(RD3, "narrname", null) + .withResourceField(RD3, "narrcreate", null) + .withResourceField(RD3, "perm", "None") + .withResourceField(RD3, "description", "my desc") + .withResourceField(RD3, "moddate", 1540606613000L) + .withResourceField(RD5, "name", "name5") + .withResourceField(RD5, "public", false) + .withResourceField(RD5, "narrname", "narr_name") + .withResourceField(RD5, "narrcreate", 1500000000000L) + .withResourceField(RD5, "perm", "None") + .withResourceField(RD5, "description", null) + .withResourceField(RD5, "moddate", 0L) + .withResourceField(RD7, "name", "name7") + .withResourceField(RD7, "public", false) + .withResourceField(RD7, "narrname", null) + .withResourceField(RD7, "narrcreate", null) + .withResourceField(RD7, "perm", "None") + .withResourceField(RD7, "description", "my desc7") + .withResourceField(RD7, "moddate", 31535999000L) + .withResourceField(RD10, "name", "name10") + .withResourceField(RD10, "public", true) + .withResourceField(RD10, "narrname", null) + .withResourceField(RD10, "narrcreate", null) + .withResourceField(RD10, "perm", "None") + .withResourceField(RD10, "description", "my desc10") + .withResourceField(RD10, "moddate", 1500000000000L) + .withResourceField(RD11, "name", "name11") + .withResourceField(RD11, "public", true) + .withResourceField(RD11, "narrname", null) + .withResourceField(RD11, "narrcreate", null) + .withResourceField(RD11, "perm", "None") + .withResourceField(RD11, "description", "my desc11") + .withResourceField(RD11, "moddate", 549864182000L) + .build()); + } + @Test public void getResourceInformationAdminPublic() throws Exception { getResourceInformation( @@ -387,6 +439,36 @@ public void getResourceInformationAdminPublic() throws Exception { .build()); } + @Test + public void getResourceInformationAdminPublicAnonUser() throws Exception { + getResourceInformation( + null, + ResourceAccess.ADMINISTRATED_AND_PUBLIC, + ResourceInformationSet.getBuilder(null) + .withNonexistentResource(RD9) + .withNonexistentResource(RD20) + .withNonexistentResource(RD21) + .withNonexistentResource(RD30) + .withNonexistentResource(RD31) + .withNonexistentResource(RD40) + .withNonexistentResource(RD41) + .withResourceField(RD10, "name", "name10") + .withResourceField(RD10, "public", true) + .withResourceField(RD10, "narrname", null) + .withResourceField(RD10, "narrcreate", null) + .withResourceField(RD10, "perm", "None") + .withResourceField(RD10, "description", "my desc10") + .withResourceField(RD10, "moddate", 1500000000000L) + .withResourceField(RD11, "name", "name11") + .withResourceField(RD11, "public", true) + .withResourceField(RD11, "narrname", null) + .withResourceField(RD11, "narrcreate", null) + .withResourceField(RD11, "perm", "None") + .withResourceField(RD11, "description", "my desc11") + .withResourceField(RD11, "moddate", 549864182000L) + .build()); + } + @Test public void getResourceInformationAdmin() throws Exception { getResourceInformation( @@ -413,40 +495,16 @@ public void getResourceInformationAdmin() throws Exception { } @Test - public void getResourceInformationAnonUser() throws Exception { - getResourceInformationAnonUser(ResourceAccess.ADMINISTRATED_AND_PUBLIC); - } - - private void getResourceInformationAnonUser(final ResourceAccess access) - throws Exception { + public void getResourceInformationAdminAnonUser() throws Exception { getResourceInformation( null, - access, + ResourceAccess.ADMINISTRATED, ResourceInformationSet.getBuilder(null) - .withNonexistentResource(RD9) .withNonexistentResource(RD20) .withNonexistentResource(RD21) - .withNonexistentResource(RD30) - .withNonexistentResource(RD31) - .withNonexistentResource(RD40) - .withNonexistentResource(RD41) - .withResourceField(RD10, "name", "name10") - .withResourceField(RD10, "public", true) - .withResourceField(RD10, "narrname", null) - .withResourceField(RD10, "narrcreate", null) - .withResourceField(RD10, "perm", "None") - .withResourceField(RD10, "description", "my desc10") - .withResourceField(RD10, "moddate", 1500000000000L) - .withResourceField(RD11, "name", "name11") - .withResourceField(RD11, "public", true) - .withResourceField(RD11, "narrname", null) - .withResourceField(RD11, "narrcreate", null) - .withResourceField(RD11, "perm", "None") - .withResourceField(RD11, "description", "my desc11") - .withResourceField(RD11, "moddate", 549864182000L) .build()); } - + private void getResourceInformation( final UserName user, final ResourceAccess access, From fd3f4463e105de985eacafd8af9f8deb76424b65 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 16:56:33 -0700 Subject: [PATCH 04/39] Add a storage function for getting all administrated groups for a user. Will be needed for getting all incoming requests for groups the user administrates. --- .../kbase/groups/storage/GroupsStorage.java | 7 ++ .../storage/mongo/MongoGroupsStorage.java | 27 +++++- .../mongo/MongoGroupsStorageOpsTest.java | 97 ++++++++++++++++++- .../mongo/MongoGroupsStorageStartupTest.java | 4 + 4 files changed, 130 insertions(+), 5 deletions(-) diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index dacfed03..0a1eb4fe 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -96,6 +96,13 @@ List getGroupNames(UserName user, Set groupID) */ List getMemberGroups(UserName user) throws GroupsStorageException; + /** Get the IDs of the set of groups the user administrates, including the owner. + * @param user the administrator. + * @return the group IDs. + * @throws GroupsStorageException if an error occurs contacting the storage system. + */ + Set getAdministratedGroups(UserName user) throws GroupsStorageException; + /** Get groups in the system, sorted by the group ID. * At most 100 groups are returned. * @param params the parameters for getting the groups. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 1e2d449a..c99b44f6 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -123,6 +123,8 @@ public class MongoGroupsStorage implements GroupsStorage { groups.put(Arrays.asList(Fields.GROUP_ID), IDX_UNIQ); // find by owner groups.put(Arrays.asList(Fields.GROUP_OWNER), null); + // find by admin + groups.put(Arrays.asList(Fields.GROUP_ADMINS), null); // find public groups and sort by ID (not needed?) groups.put(Arrays.asList(Fields.GROUP_IS_PRIVATE, Fields.GROUP_ID), null); // find groups by member and sort by ID @@ -621,6 +623,14 @@ private GroupIDAndName toGroupIDAndName(final Document grp) throws GroupsStorage } } + private GroupID toGroupID(final Document grp) throws GroupsStorageException { + try { + return new GroupID(grp.getString(Fields.GROUP_ID)); + } catch (MissingParameterException | IllegalParameterException e) { + throw new GroupsStorageException("Unexpected value in database: " + e.getMessage(), e); + } + } + @Override public boolean getGroupExists(final GroupID groupID) throws GroupsStorageException { requireNonNull(groupID, "groupID"); @@ -643,9 +653,24 @@ public List getMemberGroups(final UserName user) .append(Fields.MONGO_ID, 0); final Document sort = new Document(Fields.GROUP_ID, 1); - return getList(COL_GROUPS, query, projection, sort, 100000, d -> toGroupIDAndName(d)); + return getList(COL_GROUPS, query, projection, sort, 0, d -> toGroupIDAndName(d)); } + @Override + public Set getAdministratedGroups(final UserName user) + throws GroupsStorageException { + requireNonNull(user, "user"); + return new HashSet<>(getList( // could make a get set method but meh + COL_GROUPS, + new Document("$or", Arrays.asList( + new Document(Fields.GROUP_OWNER, user.getName()), + new Document(Fields.GROUP_ADMINS, user.getName()))), + new Document(Fields.GROUP_ID, 1).append(Fields.MONGO_ID, 1), + null, + 0, + d -> toGroupID(d))); + } + private static interface FnParamExcept { R apply(T t) throws GroupsStorageException; diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index b4459205..de4c56dd 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -465,10 +465,6 @@ public void illegalGroupDataInDB() throws Exception { "Unexpected value in database: creation time must be before modification time")); } - /* The limit of 100000 groups is just too many to test. The fn that does the limit is tested - * elsewhere, so we'll just trust it. - */ - @Test public void getMemberGroupsNoGroups() throws Exception { manager.storage.createGroup(Group.getBuilder( @@ -535,6 +531,99 @@ private void getMemberGroupsFail(final UserName member, final Exception expected } } + @Test + public void getAdministratedGroupsNoGroups() throws Exception { + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .build()); + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid1"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withMember(GroupUser.getBuilder(new UserName("uname1"), inst(1)).build()) + .build()); + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid2"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withAdministrator(GroupUser.getBuilder(new UserName("uname2"), inst(1)).build()) + .build()); + + assertThat("incorrect groups", + manager.storage.getAdministratedGroups(new UserName("uname1")), + is(set())); + } + + @Test + public void getAdministratedGroups() throws Exception { + manager.storage.createGroup(Group.getBuilder( + new GroupID("gidz"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withMember(GroupUser.getBuilder(new UserName("unamez"), inst(1)).build()) + .build()); + manager.storage.createGroup(Group.getBuilder( + new GroupID("gida"), new GroupName("name"), toGUser("unamex"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withMember(GroupUser.getBuilder(new UserName("uname"), inst(1)).build()) + .build()); + manager.storage.createGroup(Group.getBuilder( + new GroupID("gidm"), new GroupName("name"), toGUser("unamex"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withAdministrator(GroupUser.getBuilder(new UserName("uname"), inst(1)).build()) + .build()); + manager.storage.createGroup(Group.getBuilder( + new GroupID("gidd"), new GroupName("name"), toGUser("unamex"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withAdministrator(GroupUser.getBuilder(new UserName("unamey"), inst(1)).build()) + .build()); + + assertThat("incorrect groups", + manager.storage.getAdministratedGroups(new UserName("uname")), + is(set(new GroupID("gidm"), new GroupID("gidz")))); + } + + @Test + public void getAdministratedGroupsFailNull() throws Exception { + getAdministratedGroupsFail(null, new NullPointerException("user")); + } + + @Test + public void getAdministratedGroupsFailNullGroupID() throws Exception { + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .build()); + + manager.db.getCollection("groups").updateOne(new Document("id", "gid"), + new Document("$set", new Document("id", ""))); + + getAdministratedGroupsFail(new UserName("uname"), new GroupsStorageException( + "Unexpected value in database: 30000 Missing input parameter: group id")); + } + + @Test + public void getAdministratedGroupsFailBadGroupID() throws Exception { + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .build()); + + manager.db.getCollection("groups").updateOne(new Document("id", "gid"), + new Document("$set", new Document("id", "gId"))); + + getAdministratedGroupsFail(new UserName("uname"), new GroupsStorageException( + "Unexpected value in database: 30020 Illegal group ID: " + + "Illegal character in group id gId: I")); + } + + private void getAdministratedGroupsFail(final UserName admin, final Exception expected) { + try { + manager.storage.getAdministratedGroups(admin); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void updateGroupNoUpdate() throws Exception { final Group g = Group.getBuilder( diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java index 3b0520f8..2693c0dd 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java @@ -214,6 +214,10 @@ public void indexesGroups() { .append("key", new Document("own", 1)) .append("name", "own_1") .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("admin", 1)) + .append("name", "admin_1") + .append("ns", col), new Document("v", manager.indexVer) .append("key", new Document("_id", 1)) .append("name", "_id_") From 04e81cd491a1e960242d1edd2d7afd1920129507 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 17:44:57 -0700 Subject: [PATCH 05/39] Add a storage method to get requests associated with a list of groups --- .../kbase/groups/storage/GroupsStorage.java | 12 + .../storage/mongo/MongoGroupsStorage.java | 13 +- .../mongo/MongoGroupsStorageOpsTest.java | 225 +++++++++++++++++- 3 files changed, 248 insertions(+), 2 deletions(-) diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index 0a1eb4fe..c688ab11 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -278,6 +278,18 @@ List getRequestsByTarget( List getRequestsByGroup(GroupID groupID, GetRequestsParams params) throws GroupsStorageException; + /** Get the open requests that target a set of groups, sorted by the modification time of the + * request. + * At most 100 requests are returned. + * Requests that target a group are of type {@link RequestType#REQUEST}. + * @param groupIDs the targeted groups. + * @param params the parameters for getting the requests. + * @return the requests. + * @throws GroupsStorageException if an error occurs contacting the storage system. + */ + List getRequestsByGroups(Set groupIDs, GetRequestsParams params) + throws GroupsStorageException; + /** Check if a group has at least one open incoming * (e.g. are {@link RequestType#REQUEST}s) request later than a specified date. * @param groupID the ID of the group to check. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index c99b44f6..7a7a343d 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -1285,7 +1285,18 @@ public List getRequestsByGroup( final GetRequestsParams params) throws GroupsStorageException { checkNotNull(groupID, "groupID"); - final Document query = new Document(Fields.REQUEST_GROUP_ID, groupID.getName()) + return getRequestsByGroups(new HashSet<>(Arrays.asList(groupID)), params); + } + + @Override + public List getRequestsByGroups( + final Set groupIDs, + final GetRequestsParams params) + throws GroupsStorageException { + checkNoNullsInCollection(groupIDs, "groupIDs"); + final Document query = new Document(Fields.REQUEST_GROUP_ID, + new Document("$in", groupIDs.stream().map(i -> i.getName()) + .collect(Collectors.toList()))) .append(Fields.REQUEST_TYPE, RequestType.REQUEST.name()); return findRequests(query, params); } diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index de4c56dd..27d908e0 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -3575,7 +3575,6 @@ private void failGetRequestsByGroup( final GroupID id, final GetRequestsParams p, final Exception expected) { - try { manager.storage.getRequestsByGroup(id, p); fail("expected exception"); @@ -3584,6 +3583,230 @@ private void failGetRequestsByGroup( } } + @Test + public void getRequestsByGroups() throws Exception { + // tests including open/closed groups, sort direction, and skipping by date. + // does not test limit. + final Instant forever = Instant.ofEpochMilli(1000000000000000L); + + final GroupRequest first = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo"), new UserName("bar1"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(50000), forever) + .withModificationTime(Instant.ofEpochMilli(120000)) + .build()) + .build(); + final GroupRequest second = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo1"), new UserName("bar2"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(40000), forever) + .withModificationTime(Instant.ofEpochMilli(130000)) + .build()) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("45"))) + .build(); + final GroupRequest third = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo2"), new UserName("bar3"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(30000), forever) + .withModificationTime(Instant.ofEpochMilli(140000)) + .build()) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.m"))) + .build(); + final GroupRequest fourth = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo3"), new UserName("bar4"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) + .withModificationTime(Instant.ofEpochMilli(150000)) + .build()) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("42"))) + .build(); + // any request with a target user is excluded + final GroupRequest target = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo4"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .withType(RequestType.INVITE) + .withResourceType(new ResourceType("user")) + .withResource(ResourceDescriptor.from(new UserName("foo"))) + .build(); + // same for invite workspace requests + final GroupRequest targetws = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo5"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .withType(RequestType.INVITE) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("86"))) + .build(); + // and invite method requests + final GroupRequest targetcat = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo2"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .withType(RequestType.INVITE) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.m"))) + .build(); + final GroupRequest othergroup = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("other"), new UserName("other"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .build(); + final GroupRequest closed = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo5"), new UserName("closed"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .withModificationTime(inst(160000)) + .build()) + .withStatus(GroupRequestStatus.canceled()) + .build(); + + manager.storage.storeRequest(fourth); + manager.storage.storeRequest(target); + manager.storage.storeRequest(othergroup); + manager.storage.storeRequest(first); + manager.storage.storeRequest(targetws); + manager.storage.storeRequest(closed); + manager.storage.storeRequest(targetcat); + manager.storage.storeRequest(third); + manager.storage.storeRequest(second); + + final GetRequestsParams p = GetRequestsParams.getBuilder().build(); + final Set ids = set(new GroupID("foo"), new GroupID("foo1"), new GroupID("foo2"), + new GroupID("foo3"), new GroupID("foo4"), new GroupID("foo5"), + new GroupID("foo87")); + + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, p), is( + Arrays.asList(first, second, third, fourth))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups( + set(new GroupID("foo"), new GroupID("foo2"), new GroupID("foo87")), p), + is(Arrays.asList(first, third))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(130000)) + .build()), + is(Arrays.asList(third, fourth))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(130000)) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(third, fourth, closed))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(120000)) + .build()), + is(Arrays.asList(second, third, fourth))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(129999)) + .build()), + is(Arrays.asList(second, third, fourth))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder().withNullableSortAscending(false).build()), + is(Arrays.asList(fourth, third, second, first))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withNullableSortAscending(false) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(closed, fourth, third, second, first))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withNullableSortAscending(false) + .withNullableExcludeUpTo(inst(140000)) + .build()), + is(Arrays.asList(second, first))); + + + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(set(new GroupID("other")), p), + is(Arrays.asList(othergroup))); + + assertThat("incorrect get by group", + manager.storage.getRequestsByGroups(set(new GroupID("baz")), p), + is(Collections.emptyList())); + } + + @Test + public void getRequestsByGroupsHitLimit() throws Exception { + final Instant forever = Instant.ofEpochMilli(1000000000000000L); + + final List rtypes = Arrays.asList(new ResourceType("user"), + new ResourceType("workspace"), new ResourceType("catalogmethod")); + final List resources = Arrays.asList( + ResourceDescriptor.from(new UserName("target")), + new ResourceDescriptor(new ResourceID("1")), + new ResourceDescriptor(new ResourceAdministrativeID("m"), new ResourceID("m.m"))); + + for (int i = 1; i < 202; i++) { + final GroupRequest req = makeRequestForLimitTests( + forever, i, new GroupID("gid" + (i % 4)), new UserName("n" + i), + RequestType.REQUEST, + rtypes.get(i % 3), + resources.get(i % 3)); + manager.storage.storeRequest(req); + } + + // these should not show up + manager.storage.storeRequest(GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("gid4"), new UserName("foo"), + CreateModAndExpireTimes.getBuilder(inst(999999), forever) + .withModificationTime(inst(1030000)) + .build()) + .build()); + manager.storage.storeRequest(GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("gid4"), new UserName("foo"), + CreateModAndExpireTimes.getBuilder(inst(999999), forever) + .withModificationTime(inst(1130000)) + .build()) + .withType(RequestType.REQUEST) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("2"))) + .build()); + + assertRequestListCorrect( + r -> r.getRequester().getName(), + (s, p) -> s.getRequestsByGroups(set(new GroupID("gid0"), new GroupID("gid1"), + new GroupID("gid2"), new GroupID("gid3")), p)); + } + + @Test + public void getRequestsByGroupsFail() throws Exception { + failGetRequestsByGroups(null, GetRequestsParams.getBuilder().build(), + new NullPointerException("groupIDs")); + failGetRequestsByGroups(set(new GroupID("g"), null), + GetRequestsParams.getBuilder().build(), + new NullPointerException("Null item in collection groupIDs")); + failGetRequestsByGroups(set(), null, new NullPointerException("params")); + } + + private void failGetRequestsByGroups( + final Set groupIDs, + final GetRequestsParams p, + final Exception expected) { + try { + manager.storage.getRequestsByGroups(groupIDs, p); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void groupHasRequestsNoRequests() throws Exception { // wrong group From a676a4a7ad85c0912d989b5150782525a7e873c3 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 18:19:47 -0700 Subject: [PATCH 06/39] Add getRequestsForGroups method to core code Allows a user to get all incoming requests for groups they admin --- src/us/kbase/groups/core/Groups.java | 26 +++- src/us/kbase/test/groups/core/GroupsTest.java | 117 +++++++++++++++++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index 398005b9..5bbdb1b9 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -799,10 +799,9 @@ public List getRequestsForGroup( final GetRequestsParams params) throws UnauthorizedException, InvalidTokenException, AuthenticationException, NoSuchGroupException, GroupsStorageException { - checkNotNull(userToken, "userToken"); - checkNotNull(groupID, "groupID"); - checkNotNull(params, "params"); - final UserName user = userHandler.getUser(userToken); + requireNonNull(groupID, "groupID"); + requireNonNull(params, "params"); + final UserName user = userHandler.getUser(requireNonNull(userToken, "userToken")); final Group g = storage.getGroup(groupID); if (!g.isAdministrator(user)) { throw new UnauthorizedException(String.format( @@ -811,6 +810,25 @@ public List getRequestsForGroup( } return storage.getRequestsByGroup(groupID, params); } + + /** Get requests where the user administrates groups that are the target of the request. + * At most 100 requests are returned. + * @param userToken the user's token. + * @param params the parameters for getting the requests. + * @return the requests. + * @throws InvalidTokenException if the token is invalid. + * @throws AuthenticationException if authentication fails. + * @throws GroupsStorageException if an error occurs contacting the storage system. + */ + public List getRequestsForGroups( + final Token userToken, + final GetRequestsParams params) + throws InvalidTokenException, AuthenticationException, GroupsStorageException { + requireNonNull(params, "params"); + final UserName user = userHandler.getUser(requireNonNull(userToken, "userToken")); + final Set gids = storage.getAdministratedGroups(user); + return storage.getRequestsByGroups(gids, params); + } private Group getGroupFromKnownGoodRequest(final GroupRequest request) throws GroupsStorageException { diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 125d99d7..350207b7 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -3343,7 +3343,7 @@ public void getRequestsForGroupFailNulls() throws Exception { } @Test - public void getRequestForGroupFailInvalidToken() throws Exception { + public void getRequestsForGroupFailInvalidToken() throws Exception { final TestMocks mocks = initTestMocks(); when(mocks.userHandler.getUser(new Token("token"))).thenThrow(new InvalidTokenException()); @@ -3354,7 +3354,7 @@ public void getRequestForGroupFailInvalidToken() throws Exception { } @Test - public void getRequestForGroupFailNoSuchGroup() throws Exception { + public void getRequestsForGroupFailNoSuchGroup() throws Exception { final TestMocks mocks = initTestMocks(); when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("own")); @@ -3367,7 +3367,7 @@ public void getRequestForGroupFailNoSuchGroup() throws Exception { } @Test - public void getRequestForGroupFailNotAdmin() throws Exception { + public void getRequestsForGroupFailNotAdmin() throws Exception { final TestMocks mocks = initTestMocks(); when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("u1")); @@ -3398,6 +3398,117 @@ private void failGetRequestsForGroup( } } + @Test + public void getRequestsForGroupsEmpty() throws Exception { + final TestMocks mocks = initTestMocks(); + + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("own")); + when(mocks.storage.getAdministratedGroups(new UserName("own"))) + .thenReturn(set()); + when(mocks.storage.getRequestsByGroups(set(), GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .build())) + .thenReturn(Collections.emptyList()); + + assertThat("incorrect requests", mocks.groups.getRequestsForGroups( + new Token("token"), GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .build()), + is(Collections.emptyList())); + } + + @Test + public void getRequestsForGroups() throws Exception { + final TestMocks mocks = initTestMocks(); + + final UUID id1 = UUID.randomUUID(); + final UUID id2 = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("own")); + when(mocks.storage.getAdministratedGroups(new UserName("own"))) + .thenReturn(set(new GroupID("foo"), new GroupID("bar"))); + when(mocks.storage.getRequestsByGroups( + set(new GroupID("foo"), new GroupID("bar")), + GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .withNullableSortAscending(false) + .withNullableExcludeUpTo(inst(15000)) + .build())) + .thenReturn(Arrays.asList( + GroupRequest.getBuilder( + new RequestID(id1), new GroupID("foo"), new UserName("usera"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) + .build()) + .build(), + GroupRequest.getBuilder( + new RequestID(id2), new GroupID("bar"), new UserName("userb"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) + .withModificationTime(Instant.ofEpochMilli(25000)) + .build()) + .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) + .build() + )); + + assertThat("incorrect requests", mocks.groups.getRequestsForGroups( + new Token("token"), GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .withNullableSortAscending(false) + .withNullableExcludeUpTo(inst(15000)) + .build()), + is(Arrays.asList( + GroupRequest.getBuilder( + new RequestID(id1), new GroupID("foo"), new UserName("usera"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) + .build()) + .build(), + GroupRequest.getBuilder( + new RequestID(id2), new GroupID("bar"), new UserName("userb"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) + .withModificationTime(Instant.ofEpochMilli(25000)) + .build()) + .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) + .build() + ))); + } + + @Test + public void getRequestsForGroupsFailNulls() throws Exception { + final TestMocks mocks = initTestMocks(); + final Groups g = mocks.groups; + final GetRequestsParams p = GetRequestsParams.getBuilder().build(); + + failGetRequestsForGroups(g, null, p, new NullPointerException("userToken")); + failGetRequestsForGroups(g, new Token("t"), null, new NullPointerException("params")); + } + + @Test + public void getRequestsForGroupsFailInvalidToken() throws Exception { + final TestMocks mocks = initTestMocks(); + + when(mocks.userHandler.getUser(new Token("token"))).thenThrow(new InvalidTokenException()); + + failGetRequestsForGroups(mocks.groups, new Token("token"), + GetRequestsParams.getBuilder().build(), + new InvalidTokenException()); + } + + private void failGetRequestsForGroups( + final Groups g, + final Token t, + final GetRequestsParams params, + final Exception expected) { + try { + g.getRequestsForGroups(t, params); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void cancelRequest() throws Exception { final TestMocks mocks = initTestMocks(); From 500a6512bd350724f9b838e585834c9b362caee8 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Mon, 11 Mar 2019 19:05:20 -0700 Subject: [PATCH 07/39] Add get resources for administrated groups endpoint to the API --- README.md | 21 +++- RELEASE_NOTES.md | 5 +- src/us/kbase/groups/build/GroupsBuilder.java | 1 + .../kbase/groups/service/api/RequestAPI.java | 14 +++ .../groups/service/api/ServicePaths.java | 2 + .../SDKClientWorkspaceHandler.java | 3 +- .../groups/service/api/RequestAPITest.java | 109 ++++++++++++++++++ 7 files changed, 146 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e543fd4f..76ed2929 100644 --- a/README.md +++ b/README.md @@ -599,9 +599,9 @@ has no explicit permission to the resource and the resource is not publicly read ### Listing requests -There are three endpoints for listing requests detailed below - one for listing requests you -created, one for listing requests targeted at you, and one for listing requests targeted at -a specific group. +There are four endpoints for listing requests detailed below - one for listing requests you +created, one for listing requests targeted at you, one for listing requests targeted at +a specific group, and one for listing requests targeted at the groups you administrate. All endpoints return a maximum of 100 requests at once. @@ -663,6 +663,17 @@ RETURNS: A list of Requests. The user must be a group administrator. The requests only include those where a group administrator must take action on the request. +#### Get the list of requests that target administrated groups. + +``` +AUTHORIZATION REQUIRED +GET /request/groups[?parameters] + +RETURNS: A list of Requests. +``` + +The requests only include those where a group administrator must take action on the request. + ### Cancel a request ``` @@ -1023,8 +1034,6 @@ see /design/*.md * Cache results * Cache results of catalog service queries * Usability - * Endpoint for getting all requests targeted at groups I administrate - * Currently I have to go group by group * Text search - need product team feedback * In an ideal world this would be added to search but... * Hide groups? Since we can't delete groups we'll wind up with a bunch of crap in the groups @@ -1038,7 +1047,7 @@ see /design/*.md * Remember - skip is evil * Find groups where I'm (owner / admin / member) * Find groups where user X is an owner or admin - * Find groups where users X is a member and I'm a member + * Find groups where user X is a member and I'm a member * Find groups that contain workspaces I administrate * Find groups that contain workspace X and where I'm a group member * Find groups that contain catalog methods I own diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c6602dcf..37c9f4f2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,8 +2,9 @@ ## 0.1.6 -* Fixed a bug where resource administrators could not see their resources in private groups - for which they were not a member in the `/group/ endpoint`. +* Added the `/request/groups` endpoint. +* Resource administrators can now see their resources in private groups + for which they are not a member in the `/group/ endpoint`. ## 0.1.5 diff --git a/src/us/kbase/groups/build/GroupsBuilder.java b/src/us/kbase/groups/build/GroupsBuilder.java index b527efba..666ca469 100644 --- a/src/us/kbase/groups/build/GroupsBuilder.java +++ b/src/us/kbase/groups/build/GroupsBuilder.java @@ -131,6 +131,7 @@ private Groups buildGroups(final GroupsConfig c, final GroupsStorage storage) uh = new KBaseUserHandler( c.getAuthURL(), c.getWorkspaceAdminToken(), c.isAllowInsecureURLs()); } catch (IOException | URISyntaxException | AuthenticationException e) { + //TODO CODE check for a bad login and note the workspace token failed or throw a better error from the handler throw new GroupsConfigurationException( "Failed to create KBase user handler for auth service: " + e.getMessage(), e); } diff --git a/src/us/kbase/groups/service/api/RequestAPI.java b/src/us/kbase/groups/service/api/RequestAPI.java index 73be9419..d4ec2cb9 100644 --- a/src/us/kbase/groups/service/api/RequestAPI.java +++ b/src/us/kbase/groups/service/api/RequestAPI.java @@ -129,6 +129,20 @@ public List> getTargetedRequests( APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); } + @GET + @Path(ServicePaths.REQUEST_GROUPS) + @Produces(MediaType.APPLICATION_JSON) + public List> getRequestsForAdministratedGroups( + @HeaderParam(HEADER_TOKEN) final String token, + @QueryParam(Fields.GET_REQUESTS_EXCLUDE_UP_TO) final String excludeUpTo, + @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, + @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) + throws InvalidTokenException, NoTokenProvidedException, AuthenticationException, + IllegalParameterException, GroupsStorageException { + return toGroupRequestJSON(groups.getRequestsForGroups(getToken(token, true), + APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); + } + @PUT @Path(ServicePaths.REQUEST_CANCEL) @Produces(MediaType.APPLICATION_JSON) diff --git a/src/us/kbase/groups/service/api/ServicePaths.java b/src/us/kbase/groups/service/api/ServicePaths.java index d80fbbbc..d2ef4890 100644 --- a/src/us/kbase/groups/service/api/ServicePaths.java +++ b/src/us/kbase/groups/service/api/ServicePaths.java @@ -74,6 +74,8 @@ public class ServicePaths { public static final String REQUEST_CREATED = SEP + "created"; /** The location to list requests targeted at the user. */ public static final String REQUEST_TARGETED = SEP + "targeted"; + /** The location to list requests targeted at groups the user administrates. */ + public static final String REQUEST_GROUPS = SEP + "groups"; /** The location to determine whether groups have open requests. */ public static final String REQUEST_NEW = SEP + "groups" + SEP + "{" + Fields.IDS + "}" + SEP + "new"; diff --git a/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java b/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java index 476bbbf4..bca25071 100644 --- a/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java +++ b/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java @@ -81,7 +81,8 @@ public SDKClientWorkspaceHandler(final WorkspaceClient client) ver = client.ver(); // ensure the client has admin creds // don't think there's a way to safely ensure write creds - client.administer(new UObject(ImmutableMap.of("command", "listAdmins"))); + //TODO WS add a method to check what admin creds you have to WS and use here + client.administer(new UObject(ImmutableMap.of("command", "listModRequests"))); } catch (IOException | JsonClientException e) { throw getGeneralWSException(e); } diff --git a/src/us/kbase/test/groups/service/api/RequestAPITest.java b/src/us/kbase/test/groups/service/api/RequestAPITest.java index d431159e..f404d19a 100644 --- a/src/us/kbase/test/groups/service/api/RequestAPITest.java +++ b/src/us/kbase/test/groups/service/api/RequestAPITest.java @@ -560,6 +560,115 @@ private void failGetTargetedRequests( } } + // not really sure how to name these other than copy the params. + @Test + public void getRequestsForAdministratedGroups1() throws Exception { + final GetRequestsParams params = GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(10000)) + .withNullableIncludeClosed(true) + .build(); + getRequestsForAdministratedGroups(" 10000 ", "", "asc", params); + } + + @Test + public void getRequestsForAdministratedGroups2() throws Exception { + final GetRequestsParams params = GetRequestsParams.getBuilder().build(); + getRequestsForAdministratedGroups(null, null, "asc", params); + } + + @Test + public void getRequestsForAdministratedGroups3() throws Exception { + final GetRequestsParams params = GetRequestsParams.getBuilder() + .withNullableSortAscending(false) + .build(); + getRequestsForAdministratedGroups(null, null, "desc", params); + } + + @Test + public void getRequestsForAdministratedGroups4() throws Exception { + final GetRequestsParams params = GetRequestsParams.getBuilder().build(); + getRequestsForAdministratedGroups(null, null, null, params); + } + + @Test + public void getRequestsForAdministratedGroups5() throws Exception { + final GetRequestsParams params = GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .withNullableSortAscending(false).build(); + getRequestsForAdministratedGroups(null, "", null, params); + } + + @Test + public void getRequestsForAdministratedGroups6() throws Exception { + final GetRequestsParams params = GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .withNullableSortAscending(false).build(); + getRequestsForAdministratedGroups(null, "", "desc", params); + } + + private void getRequestsForAdministratedGroups( + final String excludeUpTo, + final String closed, + final String order, + final GetRequestsParams params) + throws Exception { + final Groups g = mock(Groups.class); + + when(g.getRequestsForGroups(new Token("t"), params)) + .thenReturn(Arrays.asList(REQ_MIN, REQ_DENIED, REQ_TARG)); + + final List> ret = new RequestAPI(g).getRequestsForAdministratedGroups( + "t", excludeUpTo, closed, order); + + assertThat("incorrect reqs", ret, is(Arrays.asList( + REQ_MIN_JSON, REQ_DENIED_JSON, REQ_TARG_JSON))); + } + + @Test + public void getRequestsForAdministratedGroupsMissingInput() throws Exception { + final Groups g = mock(Groups.class); + + failGetRequestsForAdministratedGroups(g, null, null, null, + new NoTokenProvidedException("No token provided")); + failGetRequestsForAdministratedGroups(g, " \t ", null, null, + new NoTokenProvidedException("No token provided")); + } + + @Test + public void getRequestsForAdministratedGroupsIllegalInput() throws Exception { + final Groups g = mock(Groups.class); + + failGetRequestsForAdministratedGroups(g, "t", " whoo" , null, + new IllegalParameterException("Invalid epoch ms: whoo")); + failGetRequestsForAdministratedGroups(g, "t", null, "but mommy ", + new IllegalParameterException("Invalid sort direction: but mommy")); + } + + @Test + public void getRequestsForAdministratedGroupsFailAuth() throws Exception { + final Groups g = mock(Groups.class); + + when(g.getRequestsForGroups(new Token("t"), GetRequestsParams.getBuilder().build())) + .thenThrow(new AuthenticationException(ErrorType.AUTHENTICATION_FAILED, "yikes")); + + failGetRequestsForAdministratedGroups(g, "t", null, null, new AuthenticationException( + ErrorType.AUTHENTICATION_FAILED, "yikes")); + } + + private void failGetRequestsForAdministratedGroups( + final Groups g, + final String token, + final String excludeUpTo, + final String order, + final Exception expected) { + try { + new RequestAPI(g).getRequestsForAdministratedGroups(token, excludeUpTo, null, order); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void cancelRequest() throws Exception { final Groups g = mock(Groups.class); From 8ad5f71d8202389c2acb48599068bee7615ae264 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 12 Mar 2019 11:51:56 -0700 Subject: [PATCH 08/39] Fix workspace handler tests --- .../groups/workspacehandler/SDKClientWorkspaceHandlerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java index ac663c6d..adc2376f 100644 --- a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java +++ b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java @@ -134,7 +134,7 @@ private void constructFail(final Exception exception) throws Exception { when(c.getURL()).thenReturn(new URL("http://bar.com")); when(c.administer(argThat(new UObjectArgumentMatcher( - ImmutableMap.of("command", "listAdmins"))))) + ImmutableMap.of("command", "listModRequests"))))) .thenThrow(exception); failConstruct(c, new ResourceHandlerException( From ec54c84058dca2337a1bc33c7b35134d93eea6eb Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 12 Mar 2019 12:26:35 -0700 Subject: [PATCH 09/39] Add resource type & id to list request parameters Limit parameter lists to those with the specified resource Also fix some urls in release notes TODO: check the resource type is valid in Groups.java Implement in MongoStorage (will need new indexes) Add to 4 endpoints query parameters --- RELEASE_NOTES.md | 12 ++-- .../kbase/groups/core/GetRequestsParams.java | 63 ++++++++++++++++++- .../groups/core/GetRequestParamsTest.java | 31 +++++++++ 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 37c9f4f2..58008851 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -23,7 +23,7 @@ ## 0.1.2 -* BACKWARDS INCOMPATIBILITY: The /request/groups//new endpoint no longer accepts a +* BACKWARDS INCOMPATIBILITY: The `/request/groups//new` endpoint no longer accepts a `laterthan` date and bases the old vs. new request determination on the last visited date for the group. @@ -32,12 +32,12 @@ * BACKWARDS INCOMPATIBILITY: The user role enum values are now capitalized like all the other enums in the API, e.g. None, Member, Admin, and Owner. -* Added a /request/id//group endpoint that returns minimal group information for Invite-type - requests. -* Added a /request/groups//new endpoint that returns whether a set of groups have +* Added a `/request/id//group` endpoint that returns minimal group information for + Invite-type requests. +* Added a `/request/groups//new` endpoint that returns whether a set of groups have open requests on a per group basis. -* Added a /group//visit endpoint that sets the last visited date for the current user for the - group, and added the last visited date to the API. +* Added a `/group//visit` endpoint that sets the last visited date for the current user for + the group, and added the last visited date to the API. ## 0.1.0 diff --git a/src/us/kbase/groups/core/GetRequestsParams.java b/src/us/kbase/groups/core/GetRequestsParams.java index 98880866..4002e5f0 100644 --- a/src/us/kbase/groups/core/GetRequestsParams.java +++ b/src/us/kbase/groups/core/GetRequestsParams.java @@ -1,10 +1,14 @@ package us.kbase.groups.core; +import static java.util.Objects.requireNonNull; + import java.time.Instant; import java.util.Optional; import us.kbase.groups.core.request.GroupRequest; import us.kbase.groups.core.request.GroupRequestStatusType; +import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceType; /** A set of parameters for use when listing {@link GroupRequest}s. * @author gaprice@lbl.gov @@ -15,14 +19,20 @@ public class GetRequestsParams { private final boolean includeClosed; private final boolean sortAscending; private final Optional excludeUpTo; + private final Optional resourceType; + private final Optional resourceID; private GetRequestsParams( final boolean includeClosed, final boolean sortAscending, - final Optional excludeUpTo) { + final Optional excludeUpTo, + final Optional resourceType, + final Optional resourceID) { this.includeClosed = includeClosed; this.sortAscending = sortAscending; this.excludeUpTo = excludeUpTo; + this.resourceType = resourceType; + this.resourceID = resourceID; } /** Get whether closed requests should be included in the list. Any request with a status type @@ -48,6 +58,24 @@ public boolean isSortAscending() { public Optional getExcludeUpTo() { return excludeUpTo; } + + /** Get the resource type that must limit the list of requests. If the type is present, + * {@link #getResourceID()} will always return a resource ID. The combination of the two + * must limit the list of requests. + * @return the resource type that all the requests must possess. + */ + public Optional getResourceType() { + return resourceType; + } + + /** Get the resource ID that must limit the list of requests. If the ID is present, + * {@link #getResourceType()} will always return a resource type. The combination of the two + * must limit the list of requests. + * @return the resource ID that all the requests must possess. + */ + public Optional getResourceID() { + return resourceID; + } @Override public int hashCode() { @@ -55,6 +83,8 @@ public int hashCode() { int result = 1; result = prime * result + ((excludeUpTo == null) ? 0 : excludeUpTo.hashCode()); result = prime * result + (includeClosed ? 1231 : 1237); + result = prime * result + ((resourceID == null) ? 0 : resourceID.hashCode()); + result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode()); result = prime * result + (sortAscending ? 1231 : 1237); return result; } @@ -81,6 +111,20 @@ public boolean equals(Object obj) { if (includeClosed != other.includeClosed) { return false; } + if (resourceID == null) { + if (other.resourceID != null) { + return false; + } + } else if (!resourceID.equals(other.resourceID)) { + return false; + } + if (resourceType == null) { + if (other.resourceType != null) { + return false; + } + } else if (!resourceType.equals(other.resourceType)) { + return false; + } if (sortAscending != other.sortAscending) { return false; } @@ -103,6 +147,8 @@ public static class Builder { private boolean includeClosed = false; private boolean sortAscending = true; private Optional excludeUpTo = Optional.empty(); + private Optional resourceType = Optional.empty(); + private Optional resourceID = Optional.empty(); private Builder() {} @@ -147,11 +193,24 @@ public Builder withNullableExcludeUpTo(final Instant excludeUpTo) { return this; } + /** Set a resource ID that limits the list of requests to include only requests + * that contain that resource ID. + * @param type the type of the resource. + * @param id the resource ID. + * @return this builder. + */ + public Builder withResource(final ResourceType type, final ResourceID id) { + this.resourceType = Optional.of(requireNonNull(type, "type")); + this.resourceID = Optional.of(requireNonNull(id, "id")); + return this; + } + /** Build the {@link GetRequestsParams}. * @return the parameters. */ public GetRequestsParams build() { - return new GetRequestsParams(includeClosed, sortAscending, excludeUpTo); + return new GetRequestsParams(includeClosed, sortAscending, excludeUpTo, + resourceType, resourceID); } } } diff --git a/src/us/kbase/test/groups/core/GetRequestParamsTest.java b/src/us/kbase/test/groups/core/GetRequestParamsTest.java index 78d8d3c7..5c4f91ab 100644 --- a/src/us/kbase/test/groups/core/GetRequestParamsTest.java +++ b/src/us/kbase/test/groups/core/GetRequestParamsTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import static us.kbase.test.groups.TestCommon.inst; import java.util.Optional; @@ -10,6 +11,9 @@ import nl.jqno.equalsverifier.EqualsVerifier; import us.kbase.groups.core.GetRequestsParams; +import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceType; +import us.kbase.test.groups.TestCommon; public class GetRequestParamsTest { @@ -25,6 +29,8 @@ public void buildMinimal() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect closed", p.isIncludeClosed(), is(false)); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -38,6 +44,8 @@ public void buildWithNulls() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect closed", p.isIncludeClosed(), is(false)); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -51,6 +59,8 @@ public void buildWithDefaults() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect closed", p.isIncludeClosed(), is(false)); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -59,11 +69,32 @@ public void buildMaximal() throws Exception { .withNullableExcludeUpTo(inst(10000)) .withNullableIncludeClosed(true) .withNullableSortAscending(false) + .withResource(new ResourceType("t"), new ResourceID("id")) .build(); assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.of(inst(10000)))); assertThat("incorrect closed", p.isIncludeClosed(), is(true)); assertThat("incorrect sort", p.isSortAscending(), is(false)); + assertThat("incorrect type", p.getResourceType(), is(Optional.of(new ResourceType("t")))); + assertThat("incorrect type", p.getResourceID(), is(Optional.of(new ResourceID("id")))); + } + + @Test + public void withResourceFailNulls() throws Exception { + withResourceFail(null, new ResourceID("i"), new NullPointerException("type")); + withResourceFail(new ResourceType("t"), null, new NullPointerException("id")); + } + + private void withResourceFail( + final ResourceType t, + final ResourceID i, + final Exception expected) { + try { + GetRequestsParams.getBuilder().withResource(t, i); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } } } From bad076b831e1fcb0d7288e4c3998ac22030f6206 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 12 Mar 2019 14:26:09 -0700 Subject: [PATCH 10/39] Check resource type is registered for the get requests endpoints --- src/us/kbase/groups/core/Groups.java | 30 +++++-- .../groups/core/request/GroupRequest.java | 1 + .../kbase/groups/service/api/GroupsAPI.java | 2 +- .../kbase/groups/service/api/RequestAPI.java | 8 +- src/us/kbase/test/groups/core/GroupsTest.java | 80 +++++++++++++++++-- 5 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index 5bbdb1b9..afd6f73e 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -741,13 +741,15 @@ public GroupView getGroupForRequest(final Token userToken, final RequestID reque * @throws InvalidTokenException if the token is invalid. * @throws AuthenticationException if authentication fails. * @throws GroupsStorageException if an error occurs contacting the storage system. + * @throws NoSuchResourceTypeException if the resource type does not exist. */ public List getRequestsForRequester( final Token userToken, final GetRequestsParams params) - throws InvalidTokenException, AuthenticationException, GroupsStorageException { + throws InvalidTokenException, AuthenticationException, GroupsStorageException, + NoSuchResourceTypeException { checkNotNull(userToken, "userToken"); - checkNotNull(params, "params"); + checkResourceRegisted(checkNotNull(params, "params")); final UserName user = userHandler.getUser(userToken); return storage.getRequestsByRequester(user, params); } @@ -761,16 +763,18 @@ public List getRequestsForRequester( * @throws AuthenticationException if authentication fails. * @throws GroupsStorageException if an error occurs contacting the storage system. * @throws ResourceHandlerException if an error occurs contacting the resource service. + * @throws NoSuchResourceTypeException if the resource type does not exist. */ public List getRequestsForTarget( final Token userToken, final GetRequestsParams params) throws InvalidTokenException, AuthenticationException, GroupsStorageException, - ResourceHandlerException { + ResourceHandlerException, NoSuchResourceTypeException { checkNotNull(userToken, "userToken"); - checkNotNull(params, "params"); + checkResourceRegisted(checkNotNull(params, "params")); final UserName user = userHandler.getUser(userToken); final Map> resources = new HashMap<>(); + // TODO CODE optimize by only fetching resources for the resource type and resource ID in params. Adds complexity to this code so YAGNI for now. for (final ResourceType t: resourceHandlers.keySet()) { final Set reslist = resourceHandlers.get(t) .getAdministratedResources(user); @@ -792,15 +796,16 @@ public List getRequestsForTarget( * @throws GroupsStorageException if an error occurs contacting the storage system. * @throws UnauthorizedException if the user is not a group admin. * @throws NoSuchGroupException if the group does not exist. + * @throws NoSuchResourceTypeException if the resource type does not exist. */ public List getRequestsForGroup( final Token userToken, final GroupID groupID, final GetRequestsParams params) throws UnauthorizedException, InvalidTokenException, AuthenticationException, - NoSuchGroupException, GroupsStorageException { + NoSuchGroupException, GroupsStorageException, NoSuchResourceTypeException { requireNonNull(groupID, "groupID"); - requireNonNull(params, "params"); + checkResourceRegisted(requireNonNull(params, "params")); final UserName user = userHandler.getUser(requireNonNull(userToken, "userToken")); final Group g = storage.getGroup(groupID); if (!g.isAdministrator(user)) { @@ -811,6 +816,13 @@ public List getRequestsForGroup( return storage.getRequestsByGroup(groupID, params); } + private void checkResourceRegisted(final GetRequestsParams params) + throws NoSuchResourceTypeException { + if (params.getResourceType().isPresent()) { + getHandler(params.getResourceType().get()); + } + } + /** Get requests where the user administrates groups that are the target of the request. * At most 100 requests are returned. * @param userToken the user's token. @@ -819,12 +831,14 @@ public List getRequestsForGroup( * @throws InvalidTokenException if the token is invalid. * @throws AuthenticationException if authentication fails. * @throws GroupsStorageException if an error occurs contacting the storage system. + * @throws NoSuchResourceTypeException if the resource type does not exist. */ public List getRequestsForGroups( final Token userToken, final GetRequestsParams params) - throws InvalidTokenException, AuthenticationException, GroupsStorageException { - requireNonNull(params, "params"); + throws InvalidTokenException, AuthenticationException, GroupsStorageException, + NoSuchResourceTypeException { + checkResourceRegisted(requireNonNull(params, "params")); final UserName user = userHandler.getUser(requireNonNull(userToken, "userToken")); final Set gids = storage.getAdministratedGroups(user); return storage.getRequestsByGroups(gids, params); diff --git a/src/us/kbase/groups/core/request/GroupRequest.java b/src/us/kbase/groups/core/request/GroupRequest.java index 89bee781..1a87ba34 100644 --- a/src/us/kbase/groups/core/request/GroupRequest.java +++ b/src/us/kbase/groups/core/request/GroupRequest.java @@ -340,6 +340,7 @@ public Builder withType(final RequestType type) { return this; } + //TODO CODE withResourcetype and withResource should be one method /** Set the type of the resource that is the target of this request. * @param resourceType the resource type. * @return this builder. diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index 8014f005..5521f7d1 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -291,7 +291,7 @@ public List> getRequestsForGroup( @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) throws InvalidTokenException, NoSuchGroupException, UnauthorizedException, AuthenticationException, MissingParameterException, IllegalParameterException, - GroupsStorageException { + GroupsStorageException, NoSuchResourceTypeException { return APICommon.toGroupRequestJSON(groups.getRequestsForGroup( getToken(token, true), new GroupID(groupID), getRequestsParams(excludeUpTo, closed, order, closed == null))); diff --git a/src/us/kbase/groups/service/api/RequestAPI.java b/src/us/kbase/groups/service/api/RequestAPI.java index d4ec2cb9..f3f54f57 100644 --- a/src/us/kbase/groups/service/api/RequestAPI.java +++ b/src/us/kbase/groups/service/api/RequestAPI.java @@ -36,6 +36,7 @@ import us.kbase.groups.core.exceptions.NoSuchGroupException; import us.kbase.groups.core.exceptions.NoSuchRequestException; import us.kbase.groups.core.exceptions.NoSuchResourceException; +import us.kbase.groups.core.exceptions.NoSuchResourceTypeException; import us.kbase.groups.core.exceptions.NoTokenProvidedException; import us.kbase.groups.core.exceptions.ResourceExistsException; import us.kbase.groups.core.exceptions.ResourceHandlerException; @@ -110,7 +111,7 @@ public List> getCreatedRequests( @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) throws InvalidTokenException, AuthenticationException, GroupsStorageException, - IllegalParameterException { + IllegalParameterException, NoSuchResourceTypeException { return toGroupRequestJSON(groups.getRequestsForRequester(getToken(token, true), APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); } @@ -124,7 +125,7 @@ public List> getTargetedRequests( @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) throws InvalidTokenException, AuthenticationException, GroupsStorageException, - IllegalParameterException, ResourceHandlerException { + IllegalParameterException, ResourceHandlerException, NoSuchResourceTypeException { return toGroupRequestJSON(groups.getRequestsForTarget(getToken(token, true), APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); } @@ -138,7 +139,8 @@ public List> getRequestsForAdministratedGroups( @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) throws InvalidTokenException, NoTokenProvidedException, AuthenticationException, - IllegalParameterException, GroupsStorageException { + IllegalParameterException, GroupsStorageException, + NoSuchResourceTypeException { return toGroupRequestJSON(groups.getRequestsForGroups(getToken(token, true), APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); } diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 350207b7..2b509e3d 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -3002,6 +3002,14 @@ private void failGetGroupForRequest( } } + /* There's 4 pretty similar methods for getting requests, and they use the same + * params class. In the Groups class there's a check that the params resource type, if + * present, is registered. Instead of 12 tests (4 methods * test without rtype, test + * with good rtype, and test with bad rtype) we write a successful test for each method. + * Two of the methods contain good rtypes and two don't. We also write a failure test + * for each method with a bad rtype. + */ + @Test public void getRequestsForRequesterEmpty() throws Exception { final TestMocks mocks = initTestMocks(); @@ -3031,6 +3039,7 @@ public void getRequestsForRequester() throws Exception { new UserName("user"), GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) .withNullableSortAscending(false) + .withResource(new ResourceType("workspace"), new ResourceID("someid")) .build())) .thenReturn(Arrays.asList( GroupRequest.getBuilder( @@ -3039,14 +3048,17 @@ public void getRequestsForRequester() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("someid"))) .build(), GroupRequest.getBuilder( - new RequestID(id2), new GroupID("gid"), new UserName("user"), + new RequestID(id2), new GroupID("gid2"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("someid"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() )); @@ -3055,6 +3067,7 @@ public void getRequestsForRequester() throws Exception { new Token("token"), GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) .withNullableSortAscending(false) + .withResource(new ResourceType("workspace"), new ResourceID("someid")) .build()), is(Arrays.asList( GroupRequest.getBuilder( @@ -3063,26 +3076,36 @@ public void getRequestsForRequester() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("someid"))) .build(), GroupRequest.getBuilder( - new RequestID(id2), new GroupID("gid"), new UserName("user"), + new RequestID(id2), new GroupID("gid2"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) + .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceDescriptor(new ResourceID("someid"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() ))); } @Test - public void getRequestsForRequesterFail() throws Exception { + public void getRequestsForRequesterFailNulls() throws Exception { failGetRequestsForRequester(null, GetRequestsParams.getBuilder().build(), new NullPointerException("userToken")); failGetRequestsForRequester(new Token("t"), null, new NullPointerException("params")); } + @Test + public void getRequestsForRequesterFailBadType() throws Exception { + failGetRequestsForRequester(new Token("t"), GetRequestsParams.getBuilder() + .withResource(new ResourceType("bad"), new ResourceID("i")).build(), + new NoSuchResourceTypeException("bad")); + } + private void failGetRequestsForRequester( final Token token, final GetRequestsParams params, @@ -3112,6 +3135,7 @@ public void getRequestsForTargetEmpty() throws Exception { GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(10000)) .withNullableIncludeClosed(true) + .withResource(new ResourceType("catalogmethod"), new ResourceID("meth")) .build())) .thenReturn(Collections.emptyList()); @@ -3119,6 +3143,7 @@ public void getRequestsForTargetEmpty() throws Exception { new Token("token"), GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(10000)) .withNullableIncludeClosed(true) + .withResource(new ResourceType("catalogmethod"), new ResourceID("meth")) .build()), is(Collections.emptyList())); } @@ -3225,12 +3250,19 @@ public void getRequestsForTarget() throws Exception { } @Test - public void getRequestsForTargetFail() throws Exception { + public void getRequestsForTargetFailNulls() throws Exception { failGetRequestsForTarget(null, GetRequestsParams.getBuilder().build(), new NullPointerException("userToken")); failGetRequestsForTarget(new Token("t"), null, new NullPointerException("params")); } + @Test + public void getRequestsForTargetFailBadType() throws Exception { + failGetRequestsForTarget(new Token("t"), GetRequestsParams.getBuilder() + .withResource(new ResourceType("bad"), new ResourceID("i")).build(), + new NoSuchResourceTypeException("bad")); + } + private void failGetRequestsForTarget( final Token token, final GetRequestsParams params, @@ -3288,6 +3320,7 @@ public void getRequestsForGroup() throws Exception { new GroupID("gid"), GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) .withNullableSortAscending(false) + .withResource(new ResourceType("catalogmethod"), new ResourceID("mod.m")) .build())) .thenReturn(Arrays.asList( GroupRequest.getBuilder( @@ -3295,6 +3328,10 @@ public void getRequestsForGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid"), new UserName("user"), @@ -3302,6 +3339,10 @@ public void getRequestsForGroup() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() )); @@ -3310,6 +3351,7 @@ public void getRequestsForGroup() throws Exception { new Token("token"), new GroupID("gid"), GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) .withNullableSortAscending(false) + .withResource(new ResourceType("catalogmethod"), new ResourceID("mod.m")) .build()), is(Arrays.asList( GroupRequest.getBuilder( @@ -3317,6 +3359,10 @@ public void getRequestsForGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid"), new UserName("user"), @@ -3324,6 +3370,10 @@ public void getRequestsForGroup() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) + .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() ))); @@ -3342,6 +3392,14 @@ public void getRequestsForGroupFailNulls() throws Exception { new NullPointerException("params")); } + @Test + public void getRequestsForGroupFailBadType() throws Exception { + failGetRequestsForGroup(initTestMocks().groups, new Token("t"), new GroupID("i"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("bad"), new ResourceID("i")).build(), + new NoSuchResourceTypeException("bad")); + } + @Test public void getRequestsForGroupFailInvalidToken() throws Exception { final TestMocks mocks = initTestMocks(); @@ -3407,12 +3465,14 @@ public void getRequestsForGroupsEmpty() throws Exception { .thenReturn(set()); when(mocks.storage.getRequestsByGroups(set(), GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) + .withResource(new ResourceType("workspace"), new ResourceID("2")) .build())) .thenReturn(Collections.emptyList()); assertThat("incorrect requests", mocks.groups.getRequestsForGroups( new Token("token"), GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) + .withResource(new ResourceType("workspace"), new ResourceID("2")) .build()), is(Collections.emptyList())); } @@ -3485,6 +3545,14 @@ public void getRequestsForGroupsFailNulls() throws Exception { failGetRequestsForGroups(g, new Token("t"), null, new NullPointerException("params")); } + @Test + public void getRequestsForGroupsFailBadType() throws Exception { + failGetRequestsForGroups(initTestMocks().groups, new Token("t"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("bad"), new ResourceID("i")).build(), + new NoSuchResourceTypeException("bad")); + } + @Test public void getRequestsForGroupsFailInvalidToken() throws Exception { final TestMocks mocks = initTestMocks(); From afdef93d2538bd50094ae182c22a7cce4a5faf60 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 12 Mar 2019 17:20:04 -0700 Subject: [PATCH 11/39] Don't allow independently setting resource & type in request Asking for stupid bugs here. Also, all the code always sets both --- src/us/kbase/groups/core/Groups.java | 3 +- .../groups/core/request/GroupRequest.java | 19 +- .../storage/mongo/MongoGroupsStorage.java | 14 +- src/us/kbase/test/groups/core/GroupsTest.java | 413 ++++++++++-------- .../groups/core/request/GroupRequestTest.java | 33 +- .../KafkaFeedsNotifierFactoryTest.java | 44 +- .../groups/service/api/APICommonTest.java | 30 +- .../groups/service/api/GroupsAPITest.java | 22 +- .../groups/service/api/RequestAPITest.java | 21 +- .../mongo/MongoGroupsStorageOpsTest.java | 194 ++++---- 10 files changed, 411 insertions(+), 382 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index afd6f73e..47b73b40 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -640,8 +640,7 @@ private GroupRequest createRequestStoreAndNotify( CreateModAndExpireTimes.getBuilder( now, now.plus(REQUEST_EXPIRE_TIME)).build()) .withType(type) - .withResourceType(resourceType) - .withResource(resource) + .withResource(resourceType, resource) .build(); storage.storeRequest(request); notifications.notify(notifyTargets, request); diff --git a/src/us/kbase/groups/core/request/GroupRequest.java b/src/us/kbase/groups/core/request/GroupRequest.java index 1a87ba34..a1cd51e1 100644 --- a/src/us/kbase/groups/core/request/GroupRequest.java +++ b/src/us/kbase/groups/core/request/GroupRequest.java @@ -1,6 +1,7 @@ package us.kbase.groups.core.request; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import java.time.Instant; import java.util.Optional; @@ -340,24 +341,14 @@ public Builder withType(final RequestType type) { return this; } - //TODO CODE withResourcetype and withResource should be one method - /** Set the type of the resource that is the target of this request. - * @param resourceType the resource type. - * @return this builder. - */ - public Builder withResourceType(final ResourceType resourceType) { - checkNotNull(resourceType, "resourceType"); - this.resourceType = resourceType; - return this; - } - /** Set the resource that is the target of this request. + * @param the type of the resource. * @param resource the resource. * @return this builder. */ - public Builder withResource(final ResourceDescriptor resource) { - checkNotNull(resource, "resource"); - this.resource = resource; + public Builder withResource(final ResourceType type, final ResourceDescriptor resource) { + this.resourceType = requireNonNull(type, "type"); + this.resource = requireNonNull(resource, "resource"); return this; } diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 7a7a343d..1ebb9244 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -1350,12 +1350,14 @@ private GroupRequest toRequest(final Document req) throws GroupsStorageException .toInstant()) .build()) .withType(RequestType.valueOf(req.getString(Fields.REQUEST_TYPE))) - .withResourceType(new ResourceType( - req.getString(Fields.REQUEST_RESOURCE_TYPE))) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID( - req.getString(Fields.REQUEST_RESOURCE_ADMINISTRATIVE_ID)), - new ResourceID(req.getString(Fields.REQUEST_RESOURCE_ID)))) + .withResource( + new ResourceType( + req.getString(Fields.REQUEST_RESOURCE_TYPE)), + new ResourceDescriptor( + new ResourceAdministrativeID( + req.getString( + Fields.REQUEST_RESOURCE_ADMINISTRATIVE_ID)), + new ResourceID(req.getString(Fields.REQUEST_RESOURCE_ID)))) .withStatus(GroupRequestStatus.from( GroupRequestStatusType.valueOf(req.getString(Fields.REQUEST_STATUS)), closedBy == null ? null : new UserName(closedBy), diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 2b509e3d..3f2800b6 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -1945,7 +1945,7 @@ public void inviteUserToGroup() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(1209610000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("foo"))) .build()); verify(mocks.notifs).notify( @@ -1956,7 +1956,8 @@ public void inviteUserToGroup() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(1209610000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("foo"))) .build() ); @@ -1966,7 +1967,7 @@ public void inviteUserToGroup() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(1209610000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("foo"))) .build() )); } @@ -2096,7 +2097,8 @@ public void inviteUserToGroupFailRequestExists() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(1209610000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("foo"))) .build() ); @@ -2154,8 +2156,8 @@ public void getRequestMembershipAdminClosed() throws Exception { public void getRequestResourceCreatorOpen() throws Exception { getRequest( new UserName("user"), - b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("78"))), + b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("78"))), set(GroupRequestUserAction.CANCEL)); } @@ -2163,8 +2165,8 @@ public void getRequestResourceCreatorOpen() throws Exception { public void getRequestResourceCreatorClosed() throws Exception { getRequest( new UserName("user"), - b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("78"))) + b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("78"))) .withStatus(GroupRequestStatus.canceled()), set()); } @@ -2173,8 +2175,8 @@ public void getRequestResourceCreatorClosed() throws Exception { public void getRequestResourceAdminOpen() throws Exception { getRequest( new UserName("own"), - b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("78"))), + b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("78"))), set(GroupRequestUserAction.ACCEPT, GroupRequestUserAction.DENY)); } @@ -2182,8 +2184,8 @@ public void getRequestResourceAdminOpen() throws Exception { public void getRequestResourceAdminClosed() throws Exception { getRequest( new UserName("admin"), - b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("78"))) + b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("78"))) .withStatus(GroupRequestStatus.expired()), set()); } @@ -2193,7 +2195,8 @@ public void getRequestInviteCreatorOpen() throws Exception { getRequest( new UserName("user"), b -> b.withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))), + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))), set(GroupRequestUserAction.CANCEL)); } @@ -2202,7 +2205,8 @@ public void getRequestInviteCreatorClosed() throws Exception { getRequest( new UserName("user"), b -> b.withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.canceled()), set()); } @@ -2212,7 +2216,8 @@ public void getRequestInviteTargetOpen() throws Exception { getRequest( new UserName("invite"), b -> b.withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))), + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))), set(GroupRequestUserAction.ACCEPT, GroupRequestUserAction.DENY)); } @@ -2221,7 +2226,8 @@ public void getRequestInviteTargetClosed() throws Exception { getRequest( new UserName("invite"), b -> b.withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.expired()), set()); } @@ -2231,8 +2237,8 @@ public void getRequestInviteResourceCreatorOpen() throws Exception { getRequest( new UserName("user"), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("87"))), + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("87"))), set(GroupRequestUserAction.CANCEL)); } @@ -2241,8 +2247,8 @@ public void getRequestInviteResourceCreatorClosed() throws Exception { getRequest( new UserName("user"), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("87"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("87"))) .withStatus(GroupRequestStatus.canceled()), set()); } @@ -2252,8 +2258,8 @@ public void getRequestInviteResourceTargetOpen() throws Exception { getRequest( new UserName("wsadmin"), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("87"))), + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("87"))), set(GroupRequestUserAction.ACCEPT, GroupRequestUserAction.DENY)); } @@ -2262,8 +2268,8 @@ public void getRequestInviteResourceTargetClosed() throws Exception { getRequest( new UserName("wsadmin"), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("87"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("87"))) .withStatus(GroupRequestStatus.expired()), set()); } @@ -2350,7 +2356,8 @@ public void getRequestFailNoSuchGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.expired()) .build()); when(mocks.storage.getGroup(new GroupID("gid"))) @@ -2395,7 +2402,8 @@ public void getRequestFailInviteCantViewAsAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2418,8 +2426,8 @@ public void getRequestFailRequestResourceCantView() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("requester"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("67"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("67"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2443,8 +2451,8 @@ public void getRequestFailInviteResourceNotAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("96"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("96"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2471,8 +2479,8 @@ public void getRequestFailInviteResourceNoSuchResource() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("96"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("96"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2499,9 +2507,9 @@ public void getRequestFailInviteResourceIllegalValue() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2531,7 +2539,8 @@ public void getRequestFailInviteUserIllegalUserName() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(new ResourceDescriptor(new ResourceID("bad*user"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("bad*user"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2558,9 +2567,9 @@ public void getRequestFailInviteResourceNoSuchResourceType() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("caterlawgmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("caterlawgmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2593,8 +2602,8 @@ public void getGroupForRequestInviteMember() throws Exception { getGroupForRequest( new UserName("target"), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(new ResourceDescriptor(new ResourceID("target")))); + .withResource(new ResourceType("user"), + new ResourceDescriptor(new ResourceID("target")))); } @Test @@ -2602,8 +2611,8 @@ public void getGroupForRequestInviteResource() throws Exception { getGroupForRequest( new UserName("wsadmin"), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("87")))); + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("87")))); } private void getGroupForRequest( @@ -2714,7 +2723,8 @@ public void getGroupForRequestFailNoSuchGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.expired()) .build()); when(mocks.storage.getGroup(new GroupID("gid"))) @@ -2759,7 +2769,8 @@ public void getGroupForRequestFailInviteCantViewAsAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2806,7 +2817,8 @@ public void getGroupForRequestFailClosed() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(new ResourceDescriptor(new ResourceID("target"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("target"))) .withStatus(GroupRequestStatus.canceled()) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( @@ -2830,8 +2842,8 @@ public void getGroupForRequestFailRequestResourceCantView() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("requester"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("67"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("67"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2855,8 +2867,8 @@ public void getGroupForRequestFailInviteResourceNotAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("96"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("96"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2883,8 +2895,8 @@ public void getGroupForRequestFailInviteResourceNoSuchResource() throws Exceptio CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("96"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("96"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2911,8 +2923,8 @@ public void getGroupForRequestFailInviteResourceIllegalValue() throws Exception CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), new ResourceID("mod.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( @@ -2943,7 +2955,8 @@ public void getGroupForRequestFailInviteUserIllegalUserName() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(new ResourceDescriptor(new ResourceID("bad*user"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("bad*user"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -2971,9 +2984,9 @@ public void getGroupForRequestFailInviteResourceNoSuchResourceType() throws Exce CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("caterlawgmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("caterlawgmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -3048,8 +3061,8 @@ public void getRequestsForRequester() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("someid"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("someid"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid2"), new UserName("user"), @@ -3057,8 +3070,8 @@ public void getRequestsForRequester() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("someid"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("someid"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() )); @@ -3076,8 +3089,8 @@ public void getRequestsForRequester() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("someid"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("someid"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid2"), new UserName("user"), @@ -3085,11 +3098,11 @@ public void getRequestsForRequester() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("someid"))) - .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) - .build() - ))); + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("someid"))) + .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) + .build() + ))); } @Test @@ -3180,7 +3193,8 @@ public void getRequestsForTarget() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid"), new UserName("user"), @@ -3189,8 +3203,8 @@ public void getRequestsForTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("24"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("24"))) .withStatus(GroupRequestStatus.accepted(new UserName("wsadmin"))) .build(), GroupRequest.getBuilder( @@ -3200,10 +3214,10 @@ public void getRequestsForTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID("mod2"), - new ResourceID("mod2.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor( + new ResourceAdministrativeID("mod2"), + new ResourceID("mod2.meth"))) .withStatus(GroupRequestStatus.canceled()) .build() )); @@ -3220,7 +3234,8 @@ public void getRequestsForTarget() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid"), new UserName("user"), @@ -3229,8 +3244,8 @@ public void getRequestsForTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("24"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("24"))) .withStatus(GroupRequestStatus.accepted(new UserName("wsadmin"))) .build(), GroupRequest.getBuilder( @@ -3240,10 +3255,10 @@ public void getRequestsForTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID("mod2"), - new ResourceID("mod2.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor( + new ResourceAdministrativeID("mod2"), + new ResourceID("mod2.meth"))) .withStatus(GroupRequestStatus.canceled()) .build() ))); @@ -3328,10 +3343,10 @@ public void getRequestsForGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID("mod"), - new ResourceID("mod.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid"), new UserName("user"), @@ -3339,10 +3354,10 @@ public void getRequestsForGroup() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID("mod"), - new ResourceID("mod.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() )); @@ -3359,10 +3374,10 @@ public void getRequestsForGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID("mod"), - new ResourceID("mod.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .build(), GroupRequest.getBuilder( new RequestID(id2), new GroupID("gid"), new UserName("user"), @@ -3370,10 +3385,10 @@ public void getRequestsForGroup() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .withModificationTime(Instant.ofEpochMilli(25000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor( - new ResourceAdministrativeID("mod"), - new ResourceID("mod.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor( + new ResourceAdministrativeID("mod"), + new ResourceID("mod.m"))) .withStatus(GroupRequestStatus.accepted(new UserName("admin"))) .build() ))); @@ -3589,7 +3604,8 @@ public void cancelRequest() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .build(), GroupRequest.getBuilder( new RequestID(id), new GroupID("gid"), new UserName("user"), @@ -3598,7 +3614,8 @@ public void cancelRequest() throws Exception { .withModificationTime(Instant.ofEpochMilli(15000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.canceled()) .build()); when(mocks.clock.instant()).thenReturn(Instant.ofEpochMilli(15000)); @@ -3616,7 +3633,8 @@ public void cancelRequest() throws Exception { .withModificationTime(Instant.ofEpochMilli(15000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.canceled()) .build())); } @@ -3656,7 +3674,8 @@ public void cancelRequestFailUnauthed() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .build()); failCancelRequest(mocks.groups, new Token("token"), new RequestID(id), @@ -3675,7 +3694,8 @@ public void cancelRequestFailClosed() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.accepted(new UserName("someguy"))) .build()); @@ -3709,16 +3729,16 @@ public void denyRequestAdminRequestMembership() throws Exception { @Test public void denyRequestAdminWhitespaceReasonRequestResource() throws Exception { denyRequestAdmin(" \t ", "admin", - b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("86")))); + b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("86")))); } @Test public void denyRequestAdminReasonInviteResource() throws Exception { denyRequestAdmin(" reason ", "wsadmin", b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("86")))); + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("86")))); } private void denyRequestAdmin( @@ -3796,7 +3816,8 @@ public void denyRequestTarget() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .build(), GroupRequest.getBuilder( new RequestID(id), new GroupID("gid"), new UserName("user"), @@ -3805,7 +3826,8 @@ public void denyRequestTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(15000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .withStatus(GroupRequestStatus.denied(new UserName("target"), "reason")) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( @@ -3832,7 +3854,8 @@ public void denyRequestTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(15000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .withStatus(GroupRequestStatus.denied(new UserName("target"), "reason")) .build()); @@ -3843,7 +3866,8 @@ public void denyRequestTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(15000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .withStatus(GroupRequestStatus.denied(new UserName("target"), "reason")) .build())); } @@ -3881,7 +3905,8 @@ public void denyRequestFailNoGroup() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("invite"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("invite"))) .withStatus(GroupRequestStatus.expired()) .build()); when(mocks.storage.getGroup(new GroupID("gid"))) @@ -3904,7 +3929,8 @@ public void denyRequestFailNotTarget() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -3924,17 +3950,17 @@ public void denyRequestFailNotAdmin() throws Exception { @Test public void denyRequestFailNotAdminRequestResource() throws Exception { - denyRequestFailNotAdmin(b -> b.withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("foo"), + denyRequestFailNotAdmin(b -> b.withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("foo"), new ResourceID("foo.bar")))); } @Test public void denyRequestFailNotAdminInviteResource() throws Exception { denyRequestFailNotAdmin(b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("foo"), - new ResourceID("foo.baz")))); + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("foo"), + new ResourceID("foo.baz")))); } private void denyRequestFailNotAdmin( @@ -3978,7 +4004,8 @@ public void denyRequestFailClosed() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .withStatus(GroupRequestStatus.canceled()) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( @@ -4004,8 +4031,8 @@ public void denyRequestFailNoSuchResource() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("56"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("56"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4033,9 +4060,9 @@ public void denyRequestFailIllegalValue() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4066,7 +4093,8 @@ public void denyRequestFailIllegalUserName() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(new ResourceDescriptor(new ResourceID("bad*user"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("bad*user"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4094,9 +4122,9 @@ public void denyRequestFailNoSuchResourceType() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("caterlogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("caterlogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4149,7 +4177,8 @@ public void acceptRequestTarget() throws Exception { acceptRequest(mocks, new UserName("target"), set(new UserName("own"), new UserName("admin"), new UserName("a3")), b -> b.withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target")))); + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target")))); final GroupUser u = GroupUser.getBuilder(new UserName("target"), inst(12000)).build(); verify(mocks.storage).addMember(new GroupID("gid"), u, inst(12000)); @@ -4164,8 +4193,8 @@ public void acceptRequestGroupAdminForRequestResource() throws Exception { acceptRequest(mocks, new UserName("admin"), set(new UserName("u1"), new UserName("u2"), new UserName("own"), new UserName("a3"), new UserName("u3")), - b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("56")))); + b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("56")))); verify(mocks.storage).addResource( new GroupID("gid"), @@ -4186,9 +4215,9 @@ public void acceptRequestResourceAdminForInviteResource() throws Exception { set(new UserName("admin"), new UserName("u4"), new UserName("own"), new UserName("a3"), new UserName("u1"), new UserName("u3")), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.n")))); + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.n")))); verify(mocks.storage).addResource( new GroupID("gid"), @@ -4282,7 +4311,8 @@ public void acceptRequestFailNotTarget() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4302,15 +4332,15 @@ public void acceptRequestFailNotAdmin() throws Exception { @Test public void acceptRequestResourceFailNotAdmin() throws Exception { - acceptRequestFailNotAdmin(b -> b.withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("34")))); + acceptRequestFailNotAdmin(b -> b.withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("34")))); } @Test public void acceptRequestResourceFailNotResourceAdmin() throws Exception { acceptRequestFailNotAdmin(b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("55")))); + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("55")))); } private void acceptRequestFailNotAdmin( @@ -4352,7 +4382,8 @@ public void acceptRequestFailClosed() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .withStatus(GroupRequestStatus.expired()) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( @@ -4409,7 +4440,8 @@ public void acceptRequestFailNoSuchGroupOnAdd() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4439,8 +4471,8 @@ public void acceptRequestFailNoSuchResourceOnRequest() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("56"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("56"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4469,9 +4501,9 @@ public void acceptRequestFailNoSuchResourceOnInvite() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("md"), - new ResourceID("md.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("md"), + new ResourceID("md.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4499,8 +4531,8 @@ public void acceptRequestFailNoSuchGroupOnAddResource() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("56"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("56"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4536,8 +4568,8 @@ public void acceptRequestFailIllegalValueOnRequest() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("4"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4568,9 +4600,9 @@ public void acceptRequestFailIllegalValueOnInvite() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("md"), - new ResourceID("md.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("md"), + new ResourceID("md.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4600,7 +4632,8 @@ public void acceptRequestFailIllegalUserNameOnRequest() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("bad*user"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("bad*user"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4629,8 +4662,8 @@ public void acceptRequestFailNoSuchResourceTypeOnRequest() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("worksperce")) - .withResource(new ResourceDescriptor(new ResourceID("4"))) + .withResource(new ResourceType("worksperce"), + new ResourceDescriptor(new ResourceID("4"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4659,7 +4692,8 @@ public void acceptRequestFailIllegalUserNameOnInvite() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResource(new ResourceDescriptor(new ResourceID("bad*user"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("bad*user"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -4689,9 +4723,9 @@ public void acceptRequestFailNoSuchResourceTypeOnInvite() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("certerlergmerthod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("md"), - new ResourceID("md.meth"))) + .withResource(new ResourceType("certerlergmerthod"), + new ResourceDescriptor(new ResourceAdministrativeID("md"), + new ResourceID("md.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -5096,9 +5130,9 @@ public void addResourceResourceAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), Instant.ofEpochMilli(1209620000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()); verify(mocks.notifs).notify( @@ -5108,9 +5142,9 @@ public void addResourceResourceAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), Instant.ofEpochMilli(1209620000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build() ); @@ -5120,9 +5154,9 @@ public void addResourceResourceAdmin() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), Instant.ofEpochMilli(1209620000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build()))); } @@ -5157,8 +5191,8 @@ public void addResourceGroupAdmin() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(1209620000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("34"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("34"))) .build()); verify(mocks.notifs).notify( @@ -5169,8 +5203,8 @@ public void addResourceGroupAdmin() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(1209620000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("34"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("34"))) .build() ); @@ -5181,8 +5215,8 @@ public void addResourceGroupAdmin() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(1209620000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("34"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("34"))) .build()))); } @@ -5568,9 +5602,9 @@ private void setReadPermissionResource(final UserName user) throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.meth"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -5608,8 +5642,8 @@ public void setReadPermissionResourceFailNoGroup() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("43"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("43"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))) .thenThrow(new NoSuchGroupException("gid")); @@ -5631,8 +5665,8 @@ public void setReadPermissionResourceFailNotAdmin() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("43"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("43"))) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( new GroupID("gid"), new GroupName("name"), toGUser("own"), @@ -5651,7 +5685,8 @@ public void setReadPermissionResourceFailInvite() throws Exception { setReadPermissionResourceFail( UUID.randomUUID(), b -> b.withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")), + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))), new UnauthorizedException( "Only Request type requests allow for resource permissions changes.")); } @@ -5660,7 +5695,8 @@ public void setReadPermissionResourceFailInvite() throws Exception { public void setReadPermissionResourceFailResourceType() throws Exception { setReadPermissionResourceFail( UUID.randomUUID(), - b -> b.withResourceType(new ResourceType("user")), + b -> b.withResource(new ResourceType("user"), + ResourceDescriptor.from(new UserName("u"))), new UnauthorizedException("Requests with a user resource type do not allow " + "for permissions changes.")); } @@ -5670,7 +5706,8 @@ public void setReadPermissionResourceFailNoResourceHandler() throws Exception { final UUID id = UUID.randomUUID(); setReadPermissionResourceFail( id, - b -> b.withResourceType(new ResourceType("wrkspce")), + b -> b.withResource(new ResourceType("wrkspce"), + new ResourceDescriptor(new ResourceID("3"))), new RuntimeException( "No handler configured for resource type wrkspce in request " + id.toString())); @@ -5712,8 +5749,8 @@ public void setReadPermissionResourceFailClosed() throws Exception { new RequestID(id), new GroupID("gid"), new UserName("user"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("43"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("43"))) .withStatus(GroupRequestStatus.denied(new UserName("d"), null)) .build()); when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( diff --git a/src/us/kbase/test/groups/core/request/GroupRequestTest.java b/src/us/kbase/test/groups/core/request/GroupRequestTest.java index a4eef4ad..a175f17c 100644 --- a/src/us/kbase/test/groups/core/request/GroupRequestTest.java +++ b/src/us/kbase/test/groups/core/request/GroupRequestTest.java @@ -70,9 +70,9 @@ public void buildWithInviteModDateAndDenied() throws Exception { .withModificationTime(Instant.ofEpochMilli(30000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("module"), - new ResourceID("module.method"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("module"), + new ResourceID("module.method"))) .withStatus(GroupRequestStatus.denied(new UserName("immean"), "targ is junky")) .build(); @@ -105,9 +105,9 @@ public void buildWithCanceled() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) - .withResourceType(new ResourceType("githubrepo")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("kbase"), - new ResourceID("kbase/groups"))) + .withResource(new ResourceType("githubrepo"), + new ResourceDescriptor(new ResourceAdministrativeID("kbase"), + new ResourceID("kbase/groups"))) .withStatus(GroupRequestStatus.canceled()) .build(); @@ -205,22 +205,21 @@ public void withTypeFail() throws Exception { } @Test - public void withResourceTypeFail() throws Exception { - try { - getBuilder().withResourceType(null); - fail("expected exception"); - } catch (Exception got) { - TestCommon.assertExceptionCorrect(got, new NullPointerException("resourceType")); - } + public void withResourceFail() throws Exception { + withResourceFail(null, new ResourceDescriptor(new ResourceID("i")), + new NullPointerException("type")); + withResourceFail(new ResourceType("t"), null, new NullPointerException("resource")); } - @Test - public void withResourceFail() throws Exception { + private void withResourceFail( + final ResourceType t, + final ResourceDescriptor d, + final Exception expected) { try { - getBuilder().withResource(null); + getBuilder().withResource(t, d); fail("expected exception"); } catch (Exception got) { - TestCommon.assertExceptionCorrect(got, new NullPointerException("resource")); + TestCommon.assertExceptionCorrect(got, expected); } } diff --git a/src/us/kbase/test/groups/notifications/KafkaFeedsNotifierFactoryTest.java b/src/us/kbase/test/groups/notifications/KafkaFeedsNotifierFactoryTest.java index 03b8f32d..d0acace7 100644 --- a/src/us/kbase/test/groups/notifications/KafkaFeedsNotifierFactoryTest.java +++ b/src/us/kbase/test/groups/notifications/KafkaFeedsNotifierFactoryTest.java @@ -225,8 +225,8 @@ public void notifyNoTargets() throws Exception { GroupRequest.getBuilder(new RequestID(UUID.randomUUID()), new GroupID("gid"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid"))) - .withResourceType(new ResourceType("rtype")) + .withResource(new ResourceType("rtype"), + new ResourceDescriptor(new ResourceID("resid"))) .withType(RequestType.REQUEST) .build()); @@ -272,8 +272,8 @@ private void notify(final RequestType rtype, final String expectedRType) throws set(new UserName("foo"), new UserName("bar")), GroupRequest.getBuilder(new RequestID(id), new GroupID("gid"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid"))) - .withResourceType(new ResourceType("user")) + .withResource(new ResourceType("user"), + new ResourceDescriptor(new ResourceID("resid"))) .withType(rtype) .build()); @@ -294,8 +294,8 @@ public void notifyFailBadResourceType() throws Exception { final GroupRequest r = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("i"), new UserName("n"), CreateModAndExpireTimes.getBuilder(inst(1), inst(2)).build()) - .withResource(new ResourceDescriptor(new ResourceID("foo"))) - .withResourceType(new ResourceType("badtype")) + .withResource(new ResourceType("badtype"), + new ResourceDescriptor(new ResourceID("foo"))) .build(); notifyFail(set(new UserName("n")), r, new IllegalArgumentException( @@ -322,8 +322,8 @@ public void acceptNoTargets() throws Exception { GroupRequest.getBuilder(new RequestID(UUID.randomUUID()), new GroupID("gid"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid"))) - .withResourceType(new ResourceType("rtype")) + .withResource(new ResourceType("rtype"), + new ResourceDescriptor(new ResourceID("resid"))) .withType(RequestType.REQUEST) .build()); @@ -361,8 +361,8 @@ public void accept() throws Exception { set(new UserName("bar"), new UserName("baz")), GroupRequest.getBuilder(new RequestID(id), new GroupID("id2"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid2"))) - .withResourceType(new ResourceType("user")) + .withResource(new ResourceType("user"), + new ResourceDescriptor(new ResourceID("resid2"))) .withType(RequestType.INVITE) .build()); @@ -384,8 +384,8 @@ public void acceptFailBadResourceType() throws Exception { final GroupRequest r = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("i"), new UserName("n"), CreateModAndExpireTimes.getBuilder(inst(1), inst(2)).build()) - .withResource(new ResourceDescriptor(new ResourceID("foo"))) - .withResourceType(new ResourceType("badtype")) + .withResource(new ResourceType("badtype"), + new ResourceDescriptor(new ResourceID("foo"))) .build(); acceptFail(set(new UserName("n")), r, new IllegalArgumentException( @@ -412,8 +412,8 @@ public void denyNoTargets() throws Exception { GroupRequest.getBuilder(new RequestID(UUID.randomUUID()), new GroupID("gid"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid"))) - .withResourceType(new ResourceType("rtype")) + .withResource(new ResourceType("rtype"), + new ResourceDescriptor(new ResourceID("resid"))) .withType(RequestType.REQUEST) .build()); @@ -452,8 +452,8 @@ public void deny() throws Exception { set(new UserName("bat"), new UserName("bang")), GroupRequest.getBuilder(new RequestID(id), new GroupID("id8"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid9"))) - .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("resid9"))) .withType(RequestType.REQUEST) .build()); @@ -474,8 +474,8 @@ public void denyFailBadResourceType() throws Exception { final GroupRequest r = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("i"), new UserName("n"), CreateModAndExpireTimes.getBuilder(inst(1), inst(2)).build()) - .withResource(new ResourceDescriptor(new ResourceID("foo"))) - .withResourceType(new ResourceType("badtype")) + .withResource(new ResourceType("badtype"), + new ResourceDescriptor(new ResourceID("foo"))) .build(); denyFail(set(new UserName("n")), r, new IllegalArgumentException( @@ -532,8 +532,8 @@ public void postFailInterrupted() throws Exception { GroupRequest.getBuilder(new RequestID(id), new GroupID("id8"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid9"))) - .withResourceType(new ResourceType("workspace")) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("resid9"))) .withType(RequestType.REQUEST) .build()); fail("expected exception"); @@ -577,8 +577,8 @@ public void postFailTimeout() throws Exception { GroupRequest.getBuilder(new RequestID(id), new GroupID("id8"), new UserName("act"), CreateModAndExpireTimes.getBuilder(inst(10000), inst(30000)).build()) - .withResource(new ResourceDescriptor(new ResourceID("resid9"))) - .withResourceType(new ResourceType("catalogmethod")) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceID("resid9"))) .withType(RequestType.INVITE) .build()); fail("expected exception"); diff --git a/src/us/kbase/test/groups/service/api/APICommonTest.java b/src/us/kbase/test/groups/service/api/APICommonTest.java index a60d0fe0..497b49ff 100644 --- a/src/us/kbase/test/groups/service/api/APICommonTest.java +++ b/src/us/kbase/test/groups/service/api/APICommonTest.java @@ -86,8 +86,7 @@ public void toGroupRequestJSON() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("n"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("n"))) .build(); assertThat("incorrect request", APICommon.toGroupRequestJSON(r), is(MapBuilder.newHashMap() @@ -114,8 +113,7 @@ public void toGroupRequestJSONWithTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("inv"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("inv"))) .withStatus(GroupRequestStatus.denied(new UserName("den"), "r")) .build(); @@ -143,8 +141,8 @@ public void toGroupRequestJSONWithWSTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("42"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("42"))) .withStatus(GroupRequestStatus.canceled()) .build(); @@ -172,8 +170,8 @@ public void toGroupRequestJSONWithInviteMethod() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceID("mod.meth"))) .withStatus(GroupRequestStatus.expired()) .build(); @@ -201,8 +199,8 @@ public void toGroupRequestJSONWithRequestMethod() throws Exception { .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceID("mod.meth"))) .withStatus(GroupRequestStatus.expired()) .build(); @@ -239,8 +237,8 @@ public void toGroupRequestJSONList() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("n1"))) + .withResource(new ResourceType("user"), + ResourceDescriptor.from(new UserName("n1"))) .build(); final UUID id2 = UUID.randomUUID(); @@ -251,8 +249,8 @@ public void toGroupRequestJSONList() throws Exception { .withModificationTime(Instant.ofEpochMilli(26000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("inv"))) + .withResource(new ResourceType("user"), + ResourceDescriptor.from(new UserName("inv"))) .withStatus(GroupRequestStatus.denied(new UserName("den"), "r")) .build(); @@ -264,8 +262,8 @@ public void toGroupRequestJSONList() throws Exception { .withModificationTime(Instant.ofEpochMilli(27000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("42"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("42"))) .withStatus(GroupRequestStatus.canceled()) .build(); diff --git a/src/us/kbase/test/groups/service/api/GroupsAPITest.java b/src/us/kbase/test/groups/service/api/GroupsAPITest.java index d951bb7c..6c93eb61 100644 --- a/src/us/kbase/test/groups/service/api/GroupsAPITest.java +++ b/src/us/kbase/test/groups/service/api/GroupsAPITest.java @@ -1061,8 +1061,8 @@ public void requestGroupMembership() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(30000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("foo"))) .build()); final Map ret = new GroupsAPI(g).requestGroupMembership("t", "gid"); @@ -1131,8 +1131,8 @@ public void inviteMember() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(30000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("bar"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("bar"))) .build()); final Map ret = new GroupsAPI(g).inviteMember("t", "gid", "bar"); @@ -1268,8 +1268,8 @@ private void getRequestsForGroup( .withModificationTime(Instant.ofEpochMilli(25000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("baz"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("baz"))) .withStatus(GroupRequestStatus.canceled()) .build() )); @@ -1678,8 +1678,8 @@ public void addResourceWithRequest() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("42"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("42"))) .build())); final Map ret = new GroupsAPI(g) @@ -1714,9 +1714,9 @@ public void addResourceWithRequestInvite() throws Exception { Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("mod"), - new ResourceID("mod.meth"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("mod"), + new ResourceID("mod.meth"))) .build())); final Map ret = new GroupsAPI(g) diff --git a/src/us/kbase/test/groups/service/api/RequestAPITest.java b/src/us/kbase/test/groups/service/api/RequestAPITest.java index f404d19a..9f67a0a4 100644 --- a/src/us/kbase/test/groups/service/api/RequestAPITest.java +++ b/src/us/kbase/test/groups/service/api/RequestAPITest.java @@ -49,7 +49,6 @@ import us.kbase.groups.core.request.RequestID; import us.kbase.groups.core.request.RequestType; import us.kbase.groups.core.resource.ResourceDescriptor; -import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.service.api.RequestAPI; import us.kbase.groups.service.api.RequestAPI.DenyRequestJSON; import us.kbase.test.groups.MapBuilder; @@ -79,8 +78,8 @@ public class RequestAPITest { .withModificationTime(Instant.ofEpochMilli(26000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("targ"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("targ"))) //TODO TEST add tests if denied state is ever visible .build(); REQ_DENIED = GroupRequest.getBuilder( @@ -90,8 +89,8 @@ public class RequestAPITest { .withModificationTime(Instant.ofEpochMilli(27000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("targ1"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("targ1"))) //TODO TEST add tests if denied state is ever visible .withStatus(GroupRequestStatus.denied(new UserName("d"), "reason")) .build(); @@ -682,8 +681,8 @@ public void cancelRequest() throws Exception { .withModificationTime(Instant.ofEpochMilli(28000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("u"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("u"))) .withStatus(GroupRequestStatus.canceled()) .build()); @@ -759,8 +758,8 @@ public void acceptRequest() throws Exception { .withModificationTime(Instant.ofEpochMilli(28000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("inv"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("inv"))) // normally the acceptor would be the same as the invited user, // but for testing purposes it's different. .withStatus(GroupRequestStatus.accepted(new UserName("inv2"))) @@ -858,8 +857,8 @@ private void denyRequest(final String expectedReason, final DenyRequestJSON body .withModificationTime(Instant.ofEpochMilli(28000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("u"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("u"))) // should not show up in output for now .withStatus(GroupRequestStatus.denied(new UserName("d"), "testreason")) .build()); diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index 27d908e0..8b54b96a 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -2439,9 +2439,9 @@ public void storeAndGetRequestMaximal() throws Exception { .withModificationTime(Instant.ofEpochMilli(50000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("githubrepo")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("kbase"), - new ResourceID("kbase/groups"))) + .withResource(new ResourceType("githubrepo"), + new ResourceDescriptor(new ResourceAdministrativeID("kbase"), + new ResourceID("kbase/groups"))) .withStatus(GroupRequestStatus.from( GroupRequestStatusType.DENIED, new UserName("whee"), "jerkface")) .build()); @@ -2454,9 +2454,9 @@ GroupRequestStatusType.DENIED, new UserName("whee"), "jerkface")) .withModificationTime(Instant.ofEpochMilli(50000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("githubrepo")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("kbase"), - new ResourceID("kbase/groups"))) + .withResource(new ResourceType("githubrepo"), + new ResourceDescriptor(new ResourceAdministrativeID("kbase"), + new ResourceID("kbase/groups"))) .withStatus(GroupRequestStatus.denied(new UserName("whee"), "jerkface")) .build())); } @@ -2501,9 +2501,9 @@ private Builder getBuilder(final UUID id) Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("yay")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("foo"), - new ResourceID("bar"))); + .withResource(new ResourceType("yay"), + new ResourceDescriptor(new ResourceAdministrativeID("foo"), + new ResourceID("bar"))); } @Test @@ -2532,7 +2532,8 @@ public void storeRequestsWithSimilarCharacteristicStrings() throws Exception { // with different resource type manager.storage.storeRequest(GroupRequest.getBuilder( new RequestID(id3), new GroupID("foo"), new UserName("bar"), times) - .withResourceType(new ResourceType("foo")) + .withResource(new ResourceType("foo"), + ResourceDescriptor.from(new UserName("bar"))) .build()); // with different resource @@ -2540,8 +2541,9 @@ public void storeRequestsWithSimilarCharacteristicStrings() throws Exception { new RequestID(id4), new GroupID("foo"), new UserName("bar"), times) // res admin ID is not included in the char string since for a given resource ID, // the admin ID is always the same - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("yay"), - new ResourceID("yo"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceAdministrativeID("yay"), + new ResourceID("yo"))) .build()); @@ -2570,14 +2572,16 @@ public void storeRequestsWithSimilarCharacteristicStrings() throws Exception { assertThat("incorrect group", manager.storage.getRequest(new RequestID(id3)), is( GroupRequest.getBuilder( new RequestID(id3), new GroupID("foo"), new UserName("bar"), times) - .withResourceType(new ResourceType("foo")) + .withResource(new ResourceType("foo"), + ResourceDescriptor.from(new UserName("bar"))) .build())); assertThat("incorrect group", manager.storage.getRequest(new RequestID(id4)), is( GroupRequest.getBuilder( new RequestID(id4), new GroupID("foo"), new UserName("bar"), times) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("yay"), - new ResourceID("yo"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceAdministrativeID("yay"), + new ResourceID("yo"))) .build())); assertThat("incorrect group", manager.storage.getRequest(new RequestID(id5)), is( @@ -2628,9 +2632,9 @@ public void storeRequestFailEquivalentRequest() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("foo")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("aid"), - new ResourceID("rid"))) + .withResource(new ResourceType("foo"), + new ResourceDescriptor(new ResourceAdministrativeID("aid"), + new ResourceID("rid"))) .build()); final GroupRequest request = GroupRequest.getBuilder( @@ -2639,11 +2643,11 @@ public void storeRequestFailEquivalentRequest() throws Exception { Instant.ofEpochMilli(30000), Instant.ofEpochMilli(40000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("foo")) // this should never actually happen. rid -> aid mapping should be immutable. // however, we test here to ensure the RAID is not included in the char string. - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("aid2"), - new ResourceID("rid"))) + .withResource(new ResourceType("foo"), + new ResourceDescriptor(new ResourceAdministrativeID("aid2"), + new ResourceID("rid"))) .build(); failStoreRequest(request, new RequestExistsException(String.format( @@ -2736,8 +2740,8 @@ public void getRequestsByRequester() throws Exception { .withModificationTime(Instant.ofEpochMilli(130000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("whee"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("whee"))) .build(); final GroupRequest third = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo3"), new UserName("bar"), @@ -2745,9 +2749,9 @@ public void getRequestsByRequester() throws Exception { .withModificationTime(Instant.ofEpochMilli(140000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m1"), - new ResourceID("m1.n"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m1"), + new ResourceID("m1.n"))) .build(); final GroupRequest fourth = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo4"), new UserName("bar"), @@ -2755,8 +2759,8 @@ public void getRequestsByRequester() throws Exception { .withModificationTime(Instant.ofEpochMilli(150000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("2"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))) .build(); final GroupRequest target = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("targ"), new UserName("whee"), @@ -2764,8 +2768,8 @@ public void getRequestsByRequester() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(new ResourceDescriptor(new ResourceID("bar"))) + .withResource(new ResourceType("user"), + new ResourceDescriptor(new ResourceID("bar"))) .build(); final GroupRequest otheruser = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("other"), new UserName("baz"), @@ -2886,8 +2890,8 @@ public void getRequestsByRequesterHitLimit() throws Exception { .withModificationTime(inst(1130000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("2"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))) .build()); assertRequestListCorrect( @@ -2924,7 +2928,7 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(120000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("bar"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) .build(); final GroupRequest second = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo2"), new UserName("whee"), @@ -2932,9 +2936,9 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(130000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.n"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.n"))) .build(); final GroupRequest third = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo3"), new UserName("whee"), @@ -2942,7 +2946,7 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(140000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("bar"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) .build(); final GroupRequest fourth = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo4"), new UserName("whee"), @@ -2950,8 +2954,8 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(150000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("1"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("1"))) .build(); final GroupRequest fifth = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo5"), new UserName("whee"), @@ -2959,7 +2963,7 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(160000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("bar"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) .build(); final GroupRequest user = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("use"), new UserName("bar"), @@ -2972,17 +2976,17 @@ public void getRequestsByTarget() throws Exception { CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), forever) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("1"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("1"))) .build(); final GroupRequest requestcat = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("reqcat"), new UserName("bar"), CreateModAndExpireTimes.getBuilder( Instant.ofEpochMilli(20000), forever) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.n"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.n"))) .build(); final GroupRequest othertarget = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("other"), new UserName("baz"), @@ -2990,7 +2994,7 @@ public void getRequestsByTarget() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("bat"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bat"))) .build(); final GroupRequest otherws = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("otherws"), new UserName("baz"), @@ -2999,8 +3003,8 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(125000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("2"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))) .build(); final GroupRequest othercat = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("othercat"), new UserName("baz"), @@ -3009,9 +3013,9 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(Instant.ofEpochMilli(135000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m2"), - new ResourceID("m2.n"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m2"), + new ResourceID("m2.n"))) .build(); final GroupRequest closed = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("closed"), new UserName("whee"), @@ -3019,7 +3023,7 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(inst(170000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("bar"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) .withStatus(GroupRequestStatus.canceled()) .build(); final GroupRequest closedws = GroupRequest.getBuilder( @@ -3028,8 +3032,8 @@ public void getRequestsByTarget() throws Exception { .withModificationTime(inst(110000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("1"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("1"))) .withStatus(GroupRequestStatus.canceled()) .build(); @@ -3216,7 +3220,8 @@ public void getRequestsByTargetHitLimit() throws Exception { .withModificationTime(inst(1030000)) .build()) .withType(RequestType.INVITE) - .withResource(ResourceDescriptor.from(new UserName("target1"))) + .withResource(GroupRequest.USER_TYPE, + ResourceDescriptor.from(new UserName("target1"))) .build()); manager.storage.storeRequest(GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("gid1"), new UserName("name1"), @@ -3224,8 +3229,8 @@ public void getRequestsByTargetHitLimit() throws Exception { .withModificationTime(inst(1130000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("5"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("5"))) .build()); manager.storage.storeRequest(GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("gid1"), new UserName("name1"), @@ -3233,9 +3238,9 @@ public void getRequestsByTargetHitLimit() throws Exception { .withModificationTime(inst(1130000)) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m5"), - new ResourceID("m5.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m5"), + new ResourceID("m5.m"))) .build()); assertRequestListCorrect( @@ -3317,25 +3322,25 @@ public void getRequestsByGroup() throws Exception { CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(40000), forever) .withModificationTime(Instant.ofEpochMilli(130000)) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("45"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("45"))) .build(); final GroupRequest third = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo"), new UserName("bar3"), CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(30000), forever) .withModificationTime(Instant.ofEpochMilli(140000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.m"))) .build(); final GroupRequest fourth = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo"), new UserName("bar4"), CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) .withModificationTime(Instant.ofEpochMilli(150000)) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("42"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("42"))) .build(); // any request with a target user is excluded final GroupRequest target = GroupRequest.getBuilder( @@ -3344,8 +3349,8 @@ public void getRequestsByGroup() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(new ResourceType("user"), + ResourceDescriptor.from(new UserName("foo"))) .build(); // same for invite workspace requests final GroupRequest targetws = GroupRequest.getBuilder( @@ -3354,8 +3359,8 @@ public void getRequestsByGroup() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("86"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("86"))) .build(); // and invite method requests final GroupRequest targetcat = GroupRequest.getBuilder( @@ -3364,9 +3369,9 @@ public void getRequestsByGroup() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.m"))) .build(); final GroupRequest othergroup = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("other"), new UserName("other"), @@ -3485,8 +3490,8 @@ public void getRequestsByGroupHitLimit() throws Exception { .withModificationTime(inst(1130000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("2"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))) .build()); assertRequestListCorrect( @@ -3558,8 +3563,7 @@ private GroupRequest makeRequestForLimitTests( .build()) .withStatus(GroupRequestStatus.from(st, new UserName("c"), "r")) .withType(type) - .withResourceType(restype) - .withResource(resource) + .withResource(restype, resource) .build(); return req; } @@ -3600,25 +3604,25 @@ public void getRequestsByGroups() throws Exception { CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(40000), forever) .withModificationTime(Instant.ofEpochMilli(130000)) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("45"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("45"))) .build(); final GroupRequest third = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo2"), new UserName("bar3"), CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(30000), forever) .withModificationTime(Instant.ofEpochMilli(140000)) .build()) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.m"))) .build(); final GroupRequest fourth = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("foo3"), new UserName("bar4"), CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) .withModificationTime(Instant.ofEpochMilli(150000)) .build()) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("42"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("42"))) .build(); // any request with a target user is excluded final GroupRequest target = GroupRequest.getBuilder( @@ -3627,8 +3631,7 @@ public void getRequestsByGroups() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("user")) - .withResource(ResourceDescriptor.from(new UserName("foo"))) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("foo"))) .build(); // same for invite workspace requests final GroupRequest targetws = GroupRequest.getBuilder( @@ -3637,8 +3640,8 @@ public void getRequestsByGroups() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("86"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("86"))) .build(); // and invite method requests final GroupRequest targetcat = GroupRequest.getBuilder( @@ -3647,9 +3650,9 @@ public void getRequestsByGroups() throws Exception { Instant.ofEpochMilli(20000), forever) .build()) .withType(RequestType.INVITE) - .withResourceType(new ResourceType("catalogmethod")) - .withResource(new ResourceDescriptor(new ResourceAdministrativeID("m"), - new ResourceID("m.m"))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.m"))) .build(); final GroupRequest othergroup = GroupRequest.getBuilder( new RequestID(UUID.randomUUID()), new GroupID("other"), new UserName("other"), @@ -3775,8 +3778,8 @@ public void getRequestsByGroupsHitLimit() throws Exception { .withModificationTime(inst(1130000)) .build()) .withType(RequestType.REQUEST) - .withResourceType(new ResourceType("workspace")) - .withResource(new ResourceDescriptor(new ResourceID("2"))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))) .build()); assertRequestListCorrect( @@ -3823,7 +3826,8 @@ public void groupHasRequestsNoRequests() throws Exception { Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) .build()) .withType(RequestType.INVITE) - .withResource(new ResourceDescriptor(new ResourceID("baz"))) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("baz"))) .build()); // closed manager.storage.storeRequest(GroupRequest.getBuilder( From a41857c7cde769d8acd5b133efe4276cf937db5a Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 12 Mar 2019 17:48:08 -0700 Subject: [PATCH 12/39] Don't allow specifying a specific resource & a list of admin ids For the same function. Doesn't make sense since they can clash and prevent any matches. --- src/us/kbase/groups/storage/GroupsStorage.java | 3 ++- src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java | 8 ++++++-- .../groups/storage/mongo/MongoGroupsStorageOpsTest.java | 6 ++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index c688ab11..9fe8aa98 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -257,7 +257,8 @@ List getRequestsByRequester(UserName requester, GetRequestsParams * At most 100 requests are returned. * @param target the targeted user. * @param resources the resources that user administrates. - * @param params the parameters for getting the requests. + * @param params the parameters for getting the requests. A particular resource may not + * be specified in the parameters. * @return the requests. * @throws GroupsStorageException if an error occurs contacting the storage system. */ diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 1ebb9244..e8d3582a 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -1258,8 +1258,12 @@ public List getRequestsByTarget( final Map> resources, final GetRequestsParams params) throws GroupsStorageException { - checkNotNull(target, "target"); - checkNotNull(resources, "resources"); + requireNonNull(target, "target"); + requireNonNull(resources, "resources"); + if (requireNonNull(params, "params").getResourceType().isPresent()) { + throw new IllegalArgumentException( + "This method may not be parameterized with a specific resource ID"); + } final List or = new LinkedList<>(); final Document query = new Document(Fields.REQUEST_TYPE, RequestType.INVITE.name()) .append("$or", or); diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index 8b54b96a..65551bfc 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -3290,6 +3290,12 @@ public void getRequestsByTargetFailBadArgs() throws Exception { failGetRequestsByTarget(new UserName("u"), Collections.emptyMap(), null, new NullPointerException("params")); + + failGetRequestsByTarget(new UserName("u"), Collections.emptyMap(), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("t"), new ResourceID("i")).build(), + new IllegalArgumentException( + "This method may not be parameterized with a specific resource ID")); } private void failGetRequestsByTarget( From 07c7a703d1116422727bc715ff552c3d6aa4649a Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 12 Mar 2019 19:14:09 -0700 Subject: [PATCH 13/39] Add a mongo method for getting requests that target a specific resource --- .../kbase/groups/storage/GroupsStorage.java | 11 + .../storage/mongo/MongoGroupsStorage.java | 27 +- .../mongo/MongoGroupsStorageOpsTest.java | 307 ++++++++++++++++-- .../mongo/MongoGroupsStorageStartupTest.java | 15 + 4 files changed, 338 insertions(+), 22 deletions(-) diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index 9fe8aa98..06e66230 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -267,6 +267,17 @@ List getRequestsByTarget( Map> resources, GetRequestsParams params) throws GroupsStorageException; + + /** Get the open requests that target a specific resource (e.g. the request type is always + * {@link RequestType#INVITE}) sorted by the modification time of the request. + * At most 100 requests are returned. + * @param params the parameters for getting the requests. A particular resource must + * be specified in the parameters. + * @return the requests. + * @throws GroupsStorageException if an error occurs contacting the storage system. + */ + List getRequestsByTarget(GetRequestsParams params) + throws GroupsStorageException; /** Get the open requests that target a group, sorted by the modification time of the request. * At most 100 requests are returned. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index e8d3582a..e1e810d5 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -149,14 +149,22 @@ public class MongoGroupsStorage implements GroupsStorage { // find by requester and state and sort/filter by modification time. requests.put(Arrays.asList(Fields.REQUEST_REQUESTER, Fields.REQUEST_STATUS, Fields.REQUEST_MODIFICATION), null); - // find by resource and sort/filter by modification time. + // find by resource admin ID and sort/filter by modification time. requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ADMINISTRATIVE_ID, Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_TYPE, Fields.REQUEST_MODIFICATION), null); - // find by resource and state and sort/filter by modification time. + // find by resource admin ID and state and sort/filter by modification time. requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ADMINISTRATIVE_ID, Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_STATUS, Fields.REQUEST_TYPE, Fields.REQUEST_MODIFICATION), null); + // find by resource ID and sort/filter by modification time. + requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, + Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_TYPE, Fields.REQUEST_MODIFICATION), + null); + // find by resource ID and state and sort/filter by modification time. + requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, + Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_STATUS, Fields.REQUEST_TYPE, + Fields.REQUEST_MODIFICATION), null); // find expired requests. requests.put(Arrays.asList(Fields.REQUEST_EXPIRATION), null); // ensure equivalent requests are rejected. See getCharacteristicString() @@ -1261,6 +1269,7 @@ public List getRequestsByTarget( requireNonNull(target, "target"); requireNonNull(resources, "resources"); if (requireNonNull(params, "params").getResourceType().isPresent()) { + // making a whole new class just to avoid this seems silly throw new IllegalArgumentException( "This method may not be parameterized with a specific resource ID"); } @@ -1282,6 +1291,20 @@ public List getRequestsByTarget( } return findRequests(query, params); } + + @Override + public List getRequestsByTarget(final GetRequestsParams params) + throws GroupsStorageException { + if (!requireNonNull(params, "params").getResourceType().isPresent()) { + // making a whole new class just to avoid this seems silly + throw new IllegalArgumentException( + "A resource must be specified in the method parameters"); + } + final Document query = new Document(Fields.REQUEST_TYPE, RequestType.INVITE.name()) + .append(Fields.REQUEST_RESOURCE_TYPE, params.getResourceType().get().getName()) + .append(Fields.REQUEST_RESOURCE_ID, params.getResourceID().get().getName()); + return findRequests(query, params); + } @Override public List getRequestsByGroup( diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index 65551bfc..0af7c698 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -3311,6 +3311,260 @@ private void failGetRequestsByTarget( } } + @Test + public void getRequestsByTargetSingleResource() throws Exception { + final Instant forever = Instant.ofEpochMilli(1000000000000000L); + + final GroupRequest first = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo1"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(50000), forever) + .withModificationTime(Instant.ofEpochMilli(120000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) + .build(); + final GroupRequest second = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo2"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(40000), forever) + .withModificationTime(Instant.ofEpochMilli(130000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) + .build(); + final GroupRequest third = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo3"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(30000), forever) + .withModificationTime(Instant.ofEpochMilli(140000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) + .build(); + final GroupRequest fourth = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo4"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) + .withModificationTime(Instant.ofEpochMilli(150000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) + .build(); + final GroupRequest fifth = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo5"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) + .withModificationTime(Instant.ofEpochMilli(160000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) + .build(); + final GroupRequest user = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("use"), new UserName("bar"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .build(); + final GroupRequest ws = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("otherws"), new UserName("baz"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .withModificationTime(Instant.ofEpochMilli(125000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))) + .build(); + final GroupRequest requestws = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("reqws"), new UserName("bar"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("1"))) + .build(); + final GroupRequest othertarget = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("other"), new UserName("baz"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), forever) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bat"))) + .build(); + final GroupRequest closed = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("closed"), new UserName("whee"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) + .withModificationTime(inst(170000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, ResourceDescriptor.from(new UserName("bar"))) + .withStatus(GroupRequestStatus.canceled()) + .build(); + + manager.storage.storeRequest(fourth); + manager.storage.storeRequest(user); + manager.storage.storeRequest(othertarget); + manager.storage.storeRequest(first); + manager.storage.storeRequest(fifth); + manager.storage.storeRequest(requestws); + manager.storage.storeRequest(closed); + manager.storage.storeRequest(third); + manager.storage.storeRequest(ws); + manager.storage.storeRequest(second); + + final GetRequestsParams p = GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .build(); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget(p), + is(Arrays.asList(first, second, third, fourth, fifth))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableExcludeUpTo(inst(130000)) + .build()), + is(Arrays.asList(third, fourth, fifth))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableExcludeUpTo(inst(130000)) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(third, fourth, fifth, closed))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableExcludeUpTo(inst(120000)) + .build()), + is(Arrays.asList(second, third, fourth, fifth))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableExcludeUpTo(inst(129999)) + .build()), + is(Arrays.asList(second, third, fourth, fifth))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableSortAscending(false) + .build()), + is(Arrays.asList(fifth, fourth, third, second, first))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableSortAscending(false) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(closed, fifth, fourth, third, second, first))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableSortAscending(false) + .withNullableExcludeUpTo(inst(140000)) + .build()), + is(Arrays.asList(second, first))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bat")) + .build()), + is(Arrays.asList(othertarget))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("2")) + .build()), + is(Arrays.asList(ws))); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("baz")) + .build()), + is(Collections.emptyList())); + + assertThat("incorrect get by target", + manager.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withResource(new ResourceType("usr"), new ResourceID("bar")) + .build()), + is(Collections.emptyList())); + } + + @Test + public void getRequestsByTargetSingleResourceHitLimit() throws Exception { + final Instant forever = Instant.ofEpochMilli(1000000000000000L); + + for (int i = 1; i < 202; i++) { + final GroupRequest req = makeRequestForLimitTests( + forever, i, new GroupID("n" + i), new UserName("name"), + RequestType.INVITE, + new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("3"))); + manager.storage.storeRequest(req); + } + + // these should not show up + manager.storage.storeRequest(GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("gid1"), new UserName("name1"), + CreateModAndExpireTimes.getBuilder(inst(999999), forever) + .withModificationTime(inst(1030000)) + .build()) + .withType(RequestType.INVITE) + .withResource(GroupRequest.USER_TYPE, + new ResourceDescriptor(new ResourceID("3"))) + .build()); + manager.storage.storeRequest(GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("gid1"), new UserName("name1"), + CreateModAndExpireTimes.getBuilder(inst(999999), forever) + .withModificationTime(inst(1130000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .build()); + + assertRequestListCorrect( + r -> r.getGroupID().getName(), + (s, p) -> s.getRequestsByTarget(p), + new ResourceType("workspace"), + new ResourceID("3")); + } + + @Test + public void getRequestsByTargetSingleResourceFailBadArgs() throws Exception { + failGetRequestsByTargetSingleResource(null, new NullPointerException("params")); + + failGetRequestsByTargetSingleResource( + GetRequestsParams.getBuilder().build(), + new IllegalArgumentException( + "A resource must be specified in the method parameters")); + } + + private void failGetRequestsByTargetSingleResource( + final GetRequestsParams params, + final Exception expected) { + try { + manager.storage.getRequestsByTarget(params); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void getRequestsByGroup() throws Exception { // tests including open/closed groups, sort direction, and skipping by date. @@ -3509,21 +3763,30 @@ private void assertRequestListCorrect( final Function getCheckString, final BiFnExcept> getRequests) throws Exception { - assertRequestListCorrect(null, 1, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(1000000), 1, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(1009999), 1, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(1010000), 2, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(1990001), 100, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(1999999), 100, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(2000000), 101, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(2019999), 102, 100, getCheckString, getRequests); - assertRequestListCorrect(inst(2020000), 103, 99, getCheckString, getRequests); - assertRequestListCorrect(inst(2499999), 150, 52, getCheckString, getRequests); - assertRequestListCorrect(inst(2999999), 200, 2, getCheckString, getRequests); - assertRequestListCorrect(inst(3000000), 201, 1, getCheckString, getRequests); - assertRequestListCorrect(inst(3000001), 201, 1, getCheckString, getRequests); - assertRequestListCorrect(inst(3010000), 201, 0, getCheckString, getRequests); - assertRequestListCorrect(inst(4000000), 201, 0, getCheckString, getRequests); + assertRequestListCorrect(getCheckString, getRequests, null, null); + } + + private void assertRequestListCorrect( + final Function getCheckString, + final BiFnExcept> getRequests, + final ResourceType t, + final ResourceID i) + throws Exception { + assertRequestListCorrect(null, 1, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1000000), 1, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1009999), 1, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1010000), 2, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1990001), 100, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1999999), 100, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2000000), 101, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2019999), 102, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2020000), 103, 99, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2499999), 150, 52, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2999999), 200, 2, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(3000000), 201, 1, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(3000001), 201, 1, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(3010000), 201, 0, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(4000000), 201, 0, getCheckString, getRequests, t, i); } private interface BiFnExcept { @@ -3536,13 +3799,17 @@ private void assertRequestListCorrect( final int start, final int size, final Function getCheckString, - final BiFnExcept> getRequests) + final BiFnExcept> getRequests, + final ResourceType type, + final ResourceID id) throws Exception { - final List res = getRequests.apply( - manager.storage, GetRequestsParams.getBuilder() + final GetRequestsParams.Builder b = GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) - .withNullableExcludeUpTo(excludeUpTo) - .build()); + .withNullableExcludeUpTo(excludeUpTo); + if (type != null) { + b.withResource(type, id); + } + final List res = getRequests.apply(manager.storage, b.build()); assertThat("incorrect size", res.size(), is(size)); int i = start; for (final GroupRequest r: res) { diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java index 2693c0dd..58debbaa 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java @@ -281,6 +281,21 @@ public void indexesRequests() { .append("mod", 1)) .append("name", "resaid_1_restype_1_status_1_type_1_mod_1") .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("resrid", 1) + .append("restype", 1) + .append("type", 1) + .append("mod", 1)) + .append("name", "resrid_1_restype_1_type_1_mod_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("resrid", 1) + .append("restype", 1) + .append("status", 1) + .append("type", 1) + .append("mod", 1)) + .append("name", "resrid_1_restype_1_status_1_type_1_mod_1") + .append("ns", col), new Document("v", manager.indexVer) .append("key", new Document("expire", 1)) .append("name", "expire_1") From 357e276992763b6f829c6af0ffc288a4a8c0547b Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 10:36:15 -0700 Subject: [PATCH 14/39] Rework getRequestsByTarget for specific resource Interplay between the standard logic & the specific resource logic was going to be too confusing, so basically separate into two effective methods --- src/us/kbase/groups/core/Groups.java | 41 +++-- .../kbase/groups/service/api/RequestAPI.java | 3 +- src/us/kbase/test/groups/core/GroupsTest.java | 148 ++++++++++++++++-- 3 files changed, 170 insertions(+), 22 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index 47b73b40..fe49f31d 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -753,7 +753,8 @@ public List getRequestsForRequester( return storage.getRequestsByRequester(user, params); } - /** Get requests where the user is the target of the request. + /** Get requests where the user is the target of the request, including requests + * associated with resources the user administrates. * At most 100 requests are returned. * @param userToken the user's token. * @param params the parameters for getting the requests. @@ -763,25 +764,41 @@ public List getRequestsForRequester( * @throws GroupsStorageException if an error occurs contacting the storage system. * @throws ResourceHandlerException if an error occurs contacting the resource service. * @throws NoSuchResourceTypeException if the resource type does not exist. + * @throws IllegalResourceIDException if the resource ID is illegal. + * @throws NoSuchResourceException if there is no such resource. + * @throws UnauthorizedException if the user is not an administrator for the resource. */ public List getRequestsForTarget( final Token userToken, final GetRequestsParams params) throws InvalidTokenException, AuthenticationException, GroupsStorageException, - ResourceHandlerException, NoSuchResourceTypeException { - checkNotNull(userToken, "userToken"); - checkResourceRegisted(checkNotNull(params, "params")); + ResourceHandlerException, NoSuchResourceTypeException, NoSuchResourceException, + IllegalResourceIDException, UnauthorizedException { + requireNonNull(userToken, "userToken"); + requireNonNull(params, "params"); final UserName user = userHandler.getUser(userToken); - final Map> resources = new HashMap<>(); - // TODO CODE optimize by only fetching resources for the resource type and resource ID in params. Adds complexity to this code so YAGNI for now. - for (final ResourceType t: resourceHandlers.keySet()) { - final Set reslist = resourceHandlers.get(t) - .getAdministratedResources(user); - if (!reslist.isEmpty()) { - resources.put(t, reslist); + final List ret; + if (params.getResourceType().isPresent()) { + final ResourceType type = params.getResourceType().get(); + final ResourceID id = params.getResourceID().get(); + if (!getHandler(type).isAdministrator(id, user)) { + throw new UnauthorizedException(String.format( + "User %s is not an admin for %s %s", + user.getName(), type.getName(), id.getName())); } + ret = storage.getRequestsByTarget(params); + } else { + final Map> resources = new HashMap<>(); + for (final ResourceType t: resourceHandlers.keySet()) { + final Set reslist = resourceHandlers.get(t) + .getAdministratedResources(user); + if (!reslist.isEmpty()) { + resources.put(t, reslist); + } + } + ret = storage.getRequestsByTarget(user, resources, params); } - return storage.getRequestsByTarget(user, resources, params); + return ret; } /** Get requests where the group is the target of the request. diff --git a/src/us/kbase/groups/service/api/RequestAPI.java b/src/us/kbase/groups/service/api/RequestAPI.java index f3f54f57..9661b108 100644 --- a/src/us/kbase/groups/service/api/RequestAPI.java +++ b/src/us/kbase/groups/service/api/RequestAPI.java @@ -125,7 +125,8 @@ public List> getTargetedRequests( @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) throws InvalidTokenException, AuthenticationException, GroupsStorageException, - IllegalParameterException, ResourceHandlerException, NoSuchResourceTypeException { + IllegalParameterException, ResourceHandlerException, NoSuchResourceTypeException, + NoSuchResourceException, IllegalResourceIDException, UnauthorizedException { return toGroupRequestJSON(groups.getRequestsForTarget(getToken(token, true), APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); } diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 3f2800b6..2e506224 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -3148,7 +3148,6 @@ public void getRequestsForTargetEmpty() throws Exception { GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(10000)) .withNullableIncludeClosed(true) - .withResource(new ResourceType("catalogmethod"), new ResourceID("meth")) .build())) .thenReturn(Collections.emptyList()); @@ -3156,7 +3155,30 @@ public void getRequestsForTargetEmpty() throws Exception { new Token("token"), GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(10000)) .withNullableIncludeClosed(true) - .withResource(new ResourceType("catalogmethod"), new ResourceID("meth")) + .build()), + is(Collections.emptyList())); + } + + @Test + public void getRequestsForTargetWithResourceEmpty() throws Exception { + final TestMocks mocks = initTestMocks(); + + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("user")); + when(mocks.wsHandler.isAdministrator(new ResourceID("22"), new UserName("user"))) + .thenReturn(true); + when(mocks.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(10000)) + .withNullableIncludeClosed(true) + .withResource(new ResourceType("workspace"), new ResourceID("22")) + .build())) + .thenReturn(Collections.emptyList()); + + assertThat("incorrect requests", mocks.groups.getRequestsForTarget( + new Token("token"), GetRequestsParams.getBuilder() + .withNullableExcludeUpTo(inst(10000)) + .withNullableIncludeClosed(true) + .withResource(new ResourceType("workspace"), new ResourceID("22")) .build()), is(Collections.emptyList())); } @@ -3264,28 +3286,136 @@ public void getRequestsForTarget() throws Exception { ))); } + @Test + public void getRequestsForTargetWithResource() throws Exception { + final TestMocks mocks = initTestMocks(); + final UUID id1 = UUID.randomUUID(); + final UUID id2 = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("target")); + when(mocks.wsHandler.isAdministrator(new ResourceID("4"), new UserName("target"))) + .thenReturn(true); + when(mocks.storage.getRequestsByTarget( + GetRequestsParams.getBuilder() + .withNullableSortAscending(false) + .withNullableExcludeUpTo(inst(10000)) + .withResource(new ResourceType("workspace"), new ResourceID("4")) + .build())) + .thenReturn(Arrays.asList( + GroupRequest.getBuilder( + new RequestID(id1), new GroupID("gid1"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .build(), + GroupRequest.getBuilder( + new RequestID(id2), new GroupID("gid2"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) + .withModificationTime(Instant.ofEpochMilli(25000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .withStatus(GroupRequestStatus.accepted(new UserName("wsadmin"))) + .build(), + GroupRequest.getBuilder( + new RequestID(id2), new GroupID("gid3"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) + .withModificationTime(Instant.ofEpochMilli(25000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .withStatus(GroupRequestStatus.canceled()) + .build() + )); + + assertThat("incorrect requests", mocks.groups.getRequestsForTarget( + new Token("token"), GetRequestsParams.getBuilder() + .withNullableSortAscending(false) + .withNullableExcludeUpTo(inst(10000)) + .withResource(new ResourceType("workspace"), new ResourceID("4")) + .build()), + is(Arrays.asList( + GroupRequest.getBuilder( + new RequestID(id1), new GroupID("gid1"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .build(), + GroupRequest.getBuilder( + new RequestID(id2), new GroupID("gid2"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) + .withModificationTime(Instant.ofEpochMilli(25000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .withStatus(GroupRequestStatus.accepted(new UserName("wsadmin"))) + .build(), + GroupRequest.getBuilder( + new RequestID(id2), new GroupID("gid3"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000)) + .withModificationTime(Instant.ofEpochMilli(25000)) + .build()) + .withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("4"))) + .withStatus(GroupRequestStatus.canceled()) + .build() + ))); + } + @Test public void getRequestsForTargetFailNulls() throws Exception { - failGetRequestsForTarget(null, GetRequestsParams.getBuilder().build(), + final Groups g = initTestMocks().groups; + + failGetRequestsForTarget(g, null, GetRequestsParams.getBuilder().build(), new NullPointerException("userToken")); - failGetRequestsForTarget(new Token("t"), null, new NullPointerException("params")); + failGetRequestsForTarget(g, new Token("t"), null, new NullPointerException("params")); } @Test public void getRequestsForTargetFailBadType() throws Exception { - failGetRequestsForTarget(new Token("t"), GetRequestsParams.getBuilder() + final TestMocks mocks = initTestMocks(); + + when(mocks.userHandler.getUser(new Token("t"))).thenReturn(new UserName("u")); + + failGetRequestsForTarget(mocks.groups, new Token("t"), GetRequestsParams.getBuilder() .withResource(new ResourceType("bad"), new ResourceID("i")).build(), new NoSuchResourceTypeException("bad")); } + @Test + public void getRequestsForTargetFailNotResourceAdmin() throws Exception { + final TestMocks mocks = initTestMocks(); + + when(mocks.userHandler.getUser(new Token("t"))).thenReturn(new UserName("u")); + when(mocks.wsHandler.isAdministrator(new ResourceID("56"), new UserName("u"))) + .thenReturn(false); + + failGetRequestsForTarget(mocks.groups, new Token("t"), GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("56")).build(), + new UnauthorizedException("User u is not an admin for workspace 56")); + } + private void failGetRequestsForTarget( + final Groups g, final Token token, final GetRequestsParams params, - final Exception expected) - throws Exception { - final TestMocks mocks = initTestMocks(); + final Exception expected) { try { - mocks.groups.getRequestsForTarget(token, params); + g.getRequestsForTarget(token, params); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); From 39e9bfd26de8aa778130b1ef124695b7008ff0af Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 14:04:37 -0700 Subject: [PATCH 15/39] Implement resource filtering for remaining request list methods in mongo Needs new indexes --- .../storage/mongo/MongoGroupsStorage.java | 27 +++- .../mongo/MongoGroupsStorageOpsTest.java | 126 +++++++++++++++--- .../mongo/MongoGroupsStorageStartupTest.java | 35 +++++ 3 files changed, 166 insertions(+), 22 deletions(-) diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index e1e810d5..4d6770c7 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -165,6 +165,22 @@ public class MongoGroupsStorage implements GroupsStorage { requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_STATUS, Fields.REQUEST_TYPE, Fields.REQUEST_MODIFICATION), null); + // find by resource ID and requester and sort/filter by modification time. + requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, Fields.REQUEST_REQUESTER, + Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_TYPE, Fields.REQUEST_MODIFICATION), + null); + // find by resource ID and requester and state and sort/filter by modification time. + requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, Fields.REQUEST_REQUESTER, + Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_STATUS, Fields.REQUEST_TYPE, + Fields.REQUEST_MODIFICATION), null); + // find by resource ID and group and sort/filter by modification time. + requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, Fields.REQUEST_GROUP_ID, + Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_TYPE, Fields.REQUEST_MODIFICATION), + null); + // find by resource ID and group and state and sort/filter by modification time. + requests.put(Arrays.asList(Fields.REQUEST_RESOURCE_ID, Fields.REQUEST_GROUP_ID, + Fields.REQUEST_RESOURCE_TYPE, Fields.REQUEST_STATUS, Fields.REQUEST_TYPE, + Fields.REQUEST_MODIFICATION), null); // find expired requests. requests.put(Arrays.asList(Fields.REQUEST_EXPIRATION), null); // ensure equivalent requests are rejected. See getCharacteristicString() @@ -1255,7 +1271,8 @@ public GroupRequest getRequest(final RequestID requestID) @Override public List getRequestsByRequester( final UserName requester, - final GetRequestsParams params) throws GroupsStorageException { + final GetRequestsParams params) + throws GroupsStorageException { checkNotNull(requester, "requester"); return findRequests(new Document(Fields.REQUEST_REQUESTER, requester.getName()), params); } @@ -1300,9 +1317,7 @@ public List getRequestsByTarget(final GetRequestsParams params) throw new IllegalArgumentException( "A resource must be specified in the method parameters"); } - final Document query = new Document(Fields.REQUEST_TYPE, RequestType.INVITE.name()) - .append(Fields.REQUEST_RESOURCE_TYPE, params.getResourceType().get().getName()) - .append(Fields.REQUEST_RESOURCE_ID, params.getResourceID().get().getName()); + final Document query = new Document(Fields.REQUEST_TYPE, RequestType.INVITE.name()); return findRequests(query, params); } @@ -1356,6 +1371,10 @@ private List findRequests(final Document query, final GetRequestsP query.append(Fields.REQUEST_MODIFICATION, new Document(inequality, Date.from(params.getExcludeUpTo().get()))); } + if (params.getResourceType().isPresent()) { + query.append(Fields.REQUEST_RESOURCE_TYPE, params.getResourceType().get().getName()) + .append(Fields.REQUEST_RESOURCE_ID, params.getResourceID().get().getName()); + } // allow other sorts? can't think of any particularly useful ones final Document sort = new Document(Fields.REQUEST_MODIFICATION, params.isSortAscending() ? 1 : -1); diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index 0af7c698..22dcd6dd 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -2829,12 +2829,12 @@ public void getRequestsByRequester() throws Exception { is(Arrays.asList(second, third, fourth))); assertThat("incorrect get by requester", - manager.storage.getRequestsByRequester(new UserName("bar"), + manager.storage.getRequestsByRequester(new UserName("bar"), GetRequestsParams.getBuilder().withNullableSortAscending(false).build()), is(Arrays.asList(fourth, third, second, first))); assertThat("incorrect get by requester", - manager.storage.getRequestsByRequester(new UserName("bar"), + manager.storage.getRequestsByRequester(new UserName("bar"), GetRequestsParams.getBuilder() .withNullableSortAscending(false) .withNullableIncludeClosed(true) @@ -2842,12 +2842,36 @@ public void getRequestsByRequester() throws Exception { is(Arrays.asList(closed, fourth, third, second, first))); assertThat("incorrect get by requester", - manager.storage.getRequestsByRequester(new UserName("bar"), + manager.storage.getRequestsByRequester(new UserName("bar"), GetRequestsParams.getBuilder() .withNullableSortAscending(false) .withNullableExcludeUpTo(inst(140000)) .build()), is(Arrays.asList(second, first))); + + assertThat("incorrect get by requester", + manager.storage.getRequestsByRequester(new UserName("bar"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .build()), + is(Arrays.asList(first))); + + assertThat("incorrect get by requester", + manager.storage.getRequestsByRequester(new UserName("bar"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(first, closed))); + + assertThat("incorrect get by requester", + manager.storage.getRequestsByRequester(new UserName("bar"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("user"), new ResourceID("bar")) + .withNullableIncludeClosed(true) + .withNullableSortAscending(false) + .build()), + is(Arrays.asList(closed, first))); assertThat("incorrect get by requester", manager.storage.getRequestsByRequester(new UserName("baz"), p), @@ -3647,9 +3671,19 @@ public void getRequestsByGroup() throws Exception { .build()) .withStatus(GroupRequestStatus.canceled()) .build(); + final GroupRequest closedws = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo"), new UserName("closedws"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) + .withModificationTime(Instant.ofEpochMilli(170000)) + .build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("45"))) + .withStatus(GroupRequestStatus.expired()) + .build(); manager.storage.storeRequest(fourth); manager.storage.storeRequest(target); + manager.storage.storeRequest(closedws); manager.storage.storeRequest(othergroup); manager.storage.storeRequest(first); manager.storage.storeRequest(targetws); @@ -3675,7 +3709,7 @@ public void getRequestsByGroup() throws Exception { .withNullableExcludeUpTo(inst(130000)) .withNullableIncludeClosed(true) .build()), - is(Arrays.asList(third, fourth, closed))); + is(Arrays.asList(third, fourth, closed, closedws))); assertThat("incorrect get by group", manager.storage.getRequestsByGroup(new GroupID("foo"), GetRequestsParams.getBuilder() @@ -3698,7 +3732,7 @@ public void getRequestsByGroup() throws Exception { .withNullableSortAscending(false) .withNullableIncludeClosed(true) .build()), - is(Arrays.asList(closed, fourth, third, second, first))); + is(Arrays.asList(closedws, closed, fourth, third, second, first))); assertThat("incorrect get by group", manager.storage.getRequestsByGroup(new GroupID("foo"), GetRequestsParams.getBuilder() @@ -3707,6 +3741,29 @@ public void getRequestsByGroup() throws Exception { .build()), is(Arrays.asList(second, first))); + assertThat("incorrect get by group", + manager.storage.getRequestsByGroup(new GroupID("foo"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("45")) + .build()), + is(Arrays.asList(second))); + + assertThat("incorrect get by group", + manager.storage.getRequestsByGroup(new GroupID("foo"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("45")) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(second, closedws))); + + assertThat("incorrect get by group", + manager.storage.getRequestsByGroup(new GroupID("foo"), + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("45")) + .withNullableIncludeClosed(true) + .withNullableSortAscending(false) + .build()), + is(Arrays.asList(closedws, second))); assertThat("incorrect get by group", manager.storage.getRequestsByGroup(new GroupID("other"), p), @@ -3941,9 +3998,19 @@ public void getRequestsByGroups() throws Exception { .build()) .withStatus(GroupRequestStatus.canceled()) .build(); + final GroupRequest closedws = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), new GroupID("foo3"), new UserName("closedws"), + CreateModAndExpireTimes.getBuilder(Instant.ofEpochMilli(20000), forever) + .withModificationTime(Instant.ofEpochMilli(170000)) + .build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("45"))) + .withStatus(GroupRequestStatus.expired()) + .build(); manager.storage.storeRequest(fourth); manager.storage.storeRequest(target); + manager.storage.storeRequest(closedws); manager.storage.storeRequest(othergroup); manager.storage.storeRequest(first); manager.storage.storeRequest(targetws); @@ -3957,50 +4024,50 @@ public void getRequestsByGroups() throws Exception { new GroupID("foo3"), new GroupID("foo4"), new GroupID("foo5"), new GroupID("foo87")); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, p), is( Arrays.asList(first, second, third, fourth))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups( set(new GroupID("foo"), new GroupID("foo2"), new GroupID("foo87")), p), is(Arrays.asList(first, third))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(130000)) .build()), is(Arrays.asList(third, fourth))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(130000)) .withNullableIncludeClosed(true) .build()), - is(Arrays.asList(third, fourth, closed))); - assertThat("incorrect get by group", + is(Arrays.asList(third, fourth, closed, closedws))); + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(120000)) .build()), is(Arrays.asList(second, third, fourth))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(129999)) .build()), is(Arrays.asList(second, third, fourth))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder().withNullableSortAscending(false).build()), is(Arrays.asList(fourth, third, second, first))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder() .withNullableSortAscending(false) .withNullableIncludeClosed(true) .build()), - is(Arrays.asList(closed, fourth, third, second, first))); - assertThat("incorrect get by group", + is(Arrays.asList(closedws, closed, fourth, third, second, first))); + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(ids, GetRequestsParams.getBuilder() .withNullableSortAscending(false) @@ -4008,12 +4075,35 @@ public void getRequestsByGroups() throws Exception { .build()), is(Arrays.asList(second, first))); + assertThat("incorrect get by groups", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("45")) + .build()), + is(Arrays.asList(second))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("45")) + .withNullableIncludeClosed(true) + .build()), + is(Arrays.asList(second, closedws))); + + assertThat("incorrect get by groups", + manager.storage.getRequestsByGroups(ids, + GetRequestsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("45")) + .withNullableIncludeClosed(true) + .withNullableSortAscending(false) + .build()), + is(Arrays.asList(closedws, second))); + + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(set(new GroupID("other")), p), is(Arrays.asList(othergroup))); - assertThat("incorrect get by group", + assertThat("incorrect get by groups", manager.storage.getRequestsByGroups(set(new GroupID("baz")), p), is(Collections.emptyList())); } diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java index 58debbaa..61723159 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java @@ -296,6 +296,41 @@ public void indexesRequests() { .append("mod", 1)) .append("name", "resrid_1_restype_1_status_1_type_1_mod_1") .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("resrid", 1) + .append("requester", 1) + .append("restype", 1) + .append("type", 1) + .append("mod", 1)) + .append("name", "resrid_1_requester_1_restype_1_type_1_mod_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("resrid", 1) + .append("requester", 1) + .append("restype", 1) + .append("status", 1) + .append("type", 1) + .append("mod", 1)) + .append("name", "resrid_1_requester_1_restype_1_status_1_type_1_mod_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("resrid", 1) + .append("gid", 1) + .append("restype", 1) + .append("type", 1) + .append("mod", 1)) + .append("name", "resrid_1_gid_1_restype_1_type_1_mod_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document("resrid", 1) + .append("gid", 1) + .append("restype", 1) + .append("status", 1) + .append("type", 1) + .append("mod", 1)) + .append("name", "resrid_1_gid_1_restype_1_status_1_type_1_mod_1") + .append("ns", col), + new Document("v", manager.indexVer) .append("key", new Document("expire", 1)) .append("name", "expire_1") From 164d242b8d9e5125d1292e7c4d3121c15a6c92de Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 14:14:42 -0700 Subject: [PATCH 16/39] Move mongo tests around Put the helper methods after the group of tests they help --- .../mongo/MongoGroupsStorageOpsTest.java | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index 22dcd6dd..ffe67c01 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -3816,88 +3816,6 @@ public void getRequestsByGroupHitLimit() throws Exception { (s, p) -> s.getRequestsByGroup(new GroupID("gid"), p)); } - private void assertRequestListCorrect( - final Function getCheckString, - final BiFnExcept> getRequests) - throws Exception { - assertRequestListCorrect(getCheckString, getRequests, null, null); - } - - private void assertRequestListCorrect( - final Function getCheckString, - final BiFnExcept> getRequests, - final ResourceType t, - final ResourceID i) - throws Exception { - assertRequestListCorrect(null, 1, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(1000000), 1, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(1009999), 1, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(1010000), 2, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(1990001), 100, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(1999999), 100, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(2000000), 101, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(2019999), 102, 100, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(2020000), 103, 99, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(2499999), 150, 52, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(2999999), 200, 2, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(3000000), 201, 1, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(3000001), 201, 1, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(3010000), 201, 0, getCheckString, getRequests, t, i); - assertRequestListCorrect(inst(4000000), 201, 0, getCheckString, getRequests, t, i); - } - - private interface BiFnExcept { - - R apply(T t, U u) throws Exception; - } - - private void assertRequestListCorrect( - final Instant excludeUpTo, - final int start, - final int size, - final Function getCheckString, - final BiFnExcept> getRequests, - final ResourceType type, - final ResourceID id) - throws Exception { - final GetRequestsParams.Builder b = GetRequestsParams.getBuilder() - .withNullableIncludeClosed(true) - .withNullableExcludeUpTo(excludeUpTo); - if (type != null) { - b.withResource(type, id); - } - final List res = getRequests.apply(manager.storage, b.build()); - assertThat("incorrect size", res.size(), is(size)); - int i = start; - for (final GroupRequest r: res) { - assertThat("incorrect request", getCheckString.apply(r), is("n" + i)); - i++; - } - } - - private GroupRequest makeRequestForLimitTests( - final Instant forever, - final int i, - final GroupID gid, - final UserName requester, - final RequestType type, - final ResourceType restype, - final ResourceDescriptor resource) - throws Exception { - final GroupRequestStatusType st = GroupRequestStatusType.values() - [i % GroupRequestStatusType.values().length]; - final GroupRequest req = GroupRequest.getBuilder( - new RequestID(UUID.randomUUID()), gid, requester, - CreateModAndExpireTimes.getBuilder(inst(1000000 - (10000 * i)), forever) - .withModificationTime(inst(1000000 + (10000 * i))) - .build()) - .withStatus(GroupRequestStatus.from(st, new UserName("c"), "r")) - .withType(type) - .withResource(restype, resource) - .build(); - return req; - } - @Test public void getRequestsByGroupFail() throws Exception { failGetRequestsByGroup(null, GetRequestsParams.getBuilder().build(), @@ -4173,6 +4091,88 @@ private void failGetRequestsByGroups( } } + private void assertRequestListCorrect( + final Function getCheckString, + final BiFnExcept> getRequests) + throws Exception { + assertRequestListCorrect(getCheckString, getRequests, null, null); + } + + private void assertRequestListCorrect( + final Function getCheckString, + final BiFnExcept> getRequests, + final ResourceType t, + final ResourceID i) + throws Exception { + assertRequestListCorrect(null, 1, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1000000), 1, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1009999), 1, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1010000), 2, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1990001), 100, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(1999999), 100, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2000000), 101, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2019999), 102, 100, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2020000), 103, 99, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2499999), 150, 52, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(2999999), 200, 2, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(3000000), 201, 1, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(3000001), 201, 1, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(3010000), 201, 0, getCheckString, getRequests, t, i); + assertRequestListCorrect(inst(4000000), 201, 0, getCheckString, getRequests, t, i); + } + + private interface BiFnExcept { + + R apply(T t, U u) throws Exception; + } + + private void assertRequestListCorrect( + final Instant excludeUpTo, + final int start, + final int size, + final Function getCheckString, + final BiFnExcept> getRequests, + final ResourceType type, + final ResourceID id) + throws Exception { + final GetRequestsParams.Builder b = GetRequestsParams.getBuilder() + .withNullableIncludeClosed(true) + .withNullableExcludeUpTo(excludeUpTo); + if (type != null) { + b.withResource(type, id); + } + final List res = getRequests.apply(manager.storage, b.build()); + assertThat("incorrect size", res.size(), is(size)); + int i = start; + for (final GroupRequest r: res) { + assertThat("incorrect request", getCheckString.apply(r), is("n" + i)); + i++; + } + } + + private GroupRequest makeRequestForLimitTests( + final Instant forever, + final int i, + final GroupID gid, + final UserName requester, + final RequestType type, + final ResourceType restype, + final ResourceDescriptor resource) + throws Exception { + final GroupRequestStatusType st = GroupRequestStatusType.values() + [i % GroupRequestStatusType.values().length]; + final GroupRequest req = GroupRequest.getBuilder( + new RequestID(UUID.randomUUID()), gid, requester, + CreateModAndExpireTimes.getBuilder(inst(1000000 - (10000 * i)), forever) + .withModificationTime(inst(1000000 + (10000 * i))) + .build()) + .withStatus(GroupRequestStatus.from(st, new UserName("c"), "r")) + .withType(type) + .withResource(restype, resource) + .build(); + return req; + } + @Test public void groupHasRequestsNoRequests() throws Exception { // wrong group From 2390874f34a64a540d9014de562c0f5f34c71c63 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 16:37:12 -0700 Subject: [PATCH 17/39] Add resource filter for request lists to API --- README.md | 5 + .../kbase/groups/service/api/APICommon.java | 18 +++ src/us/kbase/groups/service/api/Fields.java | 4 + .../kbase/groups/service/api/GroupsAPI.java | 6 +- .../kbase/groups/service/api/RequestAPI.java | 21 ++- .../groups/service/api/APICommonTest.java | 31 +++-- .../groups/service/api/GroupsAPITest.java | 45 +++++-- .../groups/service/api/RequestAPITest.java | 123 ++++++++++++++---- 8 files changed, 196 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 76ed2929..29623f8c 100644 --- a/README.md +++ b/README.md @@ -618,6 +618,11 @@ return. They all have the following optional query parameters: depending on the sort order. `asc` and `desc` sorts will include requests with modification dates, respectively, after and before the `excludeupto` date, non-inclusive. This can be used to page through the requests if needed. +* `resourcetype` - the type of a resource, for example `workspace`. If this parameter is + present `resource` must also be present. See that parameter for an explanation of the effects. +* `resource` - a resource ID, for example `56` for the `workspace` resource type. If this + parameter is present, the `resourcetype` parameter must also be present and only requests + involving that resource will be returned. Examples: diff --git a/src/us/kbase/groups/service/api/APICommon.java b/src/us/kbase/groups/service/api/APICommon.java index 03353519..779f5a67 100644 --- a/src/us/kbase/groups/service/api/APICommon.java +++ b/src/us/kbase/groups/service/api/APICommon.java @@ -203,6 +203,10 @@ public static Token getToken(final String token, final boolean required) * Otherwise they are excluded from the results. * @param sortDirection the direction of the sort - 'asc' for an ascending sort, and 'desc' * for a descending sort. + * @param resourceType the type of the resource that will be used to filter the list. The + * resource parameter must also be specified if this parameter is present. + * @param resource the ID of the resource that will be used to filter the list. The + * resourceType parameter must also be specified if this parameter is present. * @param defaultSort if sortDirection is null or whitespace only, this value is used instead. * true sets an ascending sort, false sets a descending sort. * @return the get request parameters. @@ -213,12 +217,26 @@ public static GetRequestsParams getRequestsParams( final String excludeUpTo, final String includeClosed, final String sortDirection, + final String resourceType, + final String resource, final boolean defaultSort) throws IllegalParameterException { final GetRequestsParams.Builder b = GetRequestsParams.getBuilder(); if (!isNullOrEmpty(excludeUpTo)) { b.withNullableExcludeUpTo(epochMilliStringToInstant(excludeUpTo)); } + if (isNullOrEmpty(resourceType) ^ isNullOrEmpty(resource)) { // xor + throw new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided"); + } + if (!isNullOrEmpty(resourceType)) { + try { + b.withResource(new ResourceType(resourceType), new ResourceID(resource)); + } catch (MissingParameterException e) { + throw new RuntimeException( + "Reality appears to be broken. Please turn it off then on again", e); + } + } setSortDirection(sortDirection, defaultSort, s -> b.withNullableSortAscending(s)); return b.withNullableIncludeClosed(includeClosed != null).build(); diff --git a/src/us/kbase/groups/service/api/Fields.java b/src/us/kbase/groups/service/api/Fields.java index 517ee7c2..9169d016 100644 --- a/src/us/kbase/groups/service/api/Fields.java +++ b/src/us/kbase/groups/service/api/Fields.java @@ -121,6 +121,10 @@ public class Fields { public static final String GET_REQUESTS_INCLUDE_CLOSED = "closed"; /** Set the sort order. */ public static final String GET_REQUESTS_SORT_ORDER = "order"; + /** Set the resource type with which to filter the list. */ + public static final String GET_REQUESTS_RESOURCE_TYPE = "resourcetype"; + /** Set the resource ID with which to filter the list. */ + public static final String GET_REQUESTS_RESOURCE_ID = "resource"; /* *********************** * other fields diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index 5521f7d1..83292085 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -288,13 +288,15 @@ public List> getRequestsForGroup( @PathParam(Fields.GROUP_ID) final String groupID, @QueryParam(Fields.GET_REQUESTS_EXCLUDE_UP_TO) final String excludeUpTo, @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, - @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) + @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_TYPE) final String resType, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_ID) final String resource) throws InvalidTokenException, NoSuchGroupException, UnauthorizedException, AuthenticationException, MissingParameterException, IllegalParameterException, GroupsStorageException, NoSuchResourceTypeException { return APICommon.toGroupRequestJSON(groups.getRequestsForGroup( getToken(token, true), new GroupID(groupID), - getRequestsParams(excludeUpTo, closed, order, closed == null))); + getRequestsParams(excludeUpTo, closed, order, resType, resource, closed == null))); } @DELETE diff --git a/src/us/kbase/groups/service/api/RequestAPI.java b/src/us/kbase/groups/service/api/RequestAPI.java index 9661b108..2e74dd83 100644 --- a/src/us/kbase/groups/service/api/RequestAPI.java +++ b/src/us/kbase/groups/service/api/RequestAPI.java @@ -109,11 +109,14 @@ public List> getCreatedRequests( @HeaderParam(HEADER_TOKEN) final String token, @QueryParam(Fields.GET_REQUESTS_EXCLUDE_UP_TO) final String excludeUpTo, @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, - @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) + @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_TYPE) final String resType, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_ID) final String resource) throws InvalidTokenException, AuthenticationException, GroupsStorageException, IllegalParameterException, NoSuchResourceTypeException { return toGroupRequestJSON(groups.getRequestsForRequester(getToken(token, true), - APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); + APICommon.getRequestsParams( + excludeUpTo, closed, order, resType, resource, closed == null))); } @GET @@ -123,12 +126,15 @@ public List> getTargetedRequests( @HeaderParam(HEADER_TOKEN) final String token, @QueryParam(Fields.GET_REQUESTS_EXCLUDE_UP_TO) final String excludeUpTo, @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, - @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) + @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_TYPE) final String resType, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_ID) final String resource) throws InvalidTokenException, AuthenticationException, GroupsStorageException, IllegalParameterException, ResourceHandlerException, NoSuchResourceTypeException, NoSuchResourceException, IllegalResourceIDException, UnauthorizedException { return toGroupRequestJSON(groups.getRequestsForTarget(getToken(token, true), - APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); + APICommon.getRequestsParams( + excludeUpTo, closed, order, resType, resource, closed == null))); } @GET @@ -138,12 +144,15 @@ public List> getRequestsForAdministratedGroups( @HeaderParam(HEADER_TOKEN) final String token, @QueryParam(Fields.GET_REQUESTS_EXCLUDE_UP_TO) final String excludeUpTo, @QueryParam(Fields.GET_REQUESTS_INCLUDE_CLOSED) final String closed, - @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order) + @QueryParam(Fields.GET_REQUESTS_SORT_ORDER) final String order, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_TYPE) final String resType, + @QueryParam(Fields.GET_REQUESTS_RESOURCE_ID) final String resource) throws InvalidTokenException, NoTokenProvidedException, AuthenticationException, IllegalParameterException, GroupsStorageException, NoSuchResourceTypeException { return toGroupRequestJSON(groups.getRequestsForGroups(getToken(token, true), - APICommon.getRequestsParams(excludeUpTo, closed, order, closed == null))); + APICommon.getRequestsParams( + excludeUpTo, closed, order, resType, resource, closed == null))); } @PUT diff --git a/src/us/kbase/test/groups/service/api/APICommonTest.java b/src/us/kbase/test/groups/service/api/APICommonTest.java index 497b49ff..7b022261 100644 --- a/src/us/kbase/test/groups/service/api/APICommonTest.java +++ b/src/us/kbase/test/groups/service/api/APICommonTest.java @@ -579,11 +579,13 @@ private void failGetToken(final String token, final Exception expected) { @Test public void getRequestParamsNulls() throws Exception { - final GetRequestsParams p = APICommon.getRequestsParams(null, null, null, true); + final GetRequestsParams p = APICommon.getRequestsParams( + null, null, null, null, null, true); assertThat("incorrect params", p, is(GetRequestsParams.getBuilder().build())); - final GetRequestsParams p2 = APICommon.getRequestsParams(null, null, null, false); + final GetRequestsParams p2 = APICommon.getRequestsParams( + null, null, null, null, null, false); assertThat("incorrect params", p2, is(GetRequestsParams.getBuilder() .withNullableSortAscending(false) @@ -593,13 +595,13 @@ public void getRequestParamsNulls() throws Exception { @Test public void getRequestParamsWhitespace() throws Exception { final String ws = " \t "; - final GetRequestsParams p = APICommon.getRequestsParams(ws, ws, ws, true); + final GetRequestsParams p = APICommon.getRequestsParams(ws, ws, ws, ws, ws, true); assertThat("incorrect params", p, is(GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) .build())); - final GetRequestsParams p2 = APICommon.getRequestsParams(ws, ws, ws, false); + final GetRequestsParams p2 = APICommon.getRequestsParams(ws, ws, ws, ws, ws, false); assertThat("incorrect params", p2, is(GetRequestsParams.getBuilder() .withNullableSortAscending(false) @@ -610,15 +612,16 @@ public void getRequestParamsWhitespace() throws Exception { @Test public void getRequestParamsValues() throws Exception { final GetRequestsParams p = APICommon.getRequestsParams( - " \t 12000 ", " yes ", " asc ", false); + " \t 12000 ", " yes ", " asc ", "type", "res", false); assertThat("incorrect params", p, is(GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(12000)) .withNullableIncludeClosed(true) + .withResource(new ResourceType("type"), new ResourceID("res")) .build())); final GetRequestsParams p2 = APICommon.getRequestsParams( - " \t " + Long.MAX_VALUE + " ", " no ", " desc ", true); + " \t " + Long.MAX_VALUE + " ", " no ", " desc ", null, " \t ", true); assertThat("incorrect params", p2, is(GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(Long.MAX_VALUE)) @@ -627,27 +630,35 @@ public void getRequestParamsValues() throws Exception { .build())); final GetRequestsParams p3 = APICommon.getRequestsParams( - " \t " + Long.MIN_VALUE + " ", null, null, true); + " \t " + Long.MIN_VALUE + " ", null, null, "t", "r", true); assertThat("incorrect params", p3, is(GetRequestsParams.getBuilder() .withNullableExcludeUpTo(inst(Long.MIN_VALUE)) + .withResource(new ResourceType("t"), new ResourceID("r")) .build())); } @Test public void getRequestParamsFail() throws Exception { - failGetRequestParams(" foo ", null, + failGetRequestParams(" foo ", null, null, null, new IllegalParameterException("Invalid epoch ms: foo")); - failGetRequestParams(null, "asd", new IllegalParameterException( + failGetRequestParams(null, "asd", null, null, new IllegalParameterException( "Invalid sort direction: asd")); + failGetRequestParams(null, null, "t", null, new IllegalParameterException( + "Either both or neither of the resource type and resource ID must be provided")); + failGetRequestParams(null, null, " \t ", "r", new IllegalParameterException( + "Either both or neither of the resource type and resource ID must be provided")); + } private void failGetRequestParams( final String excludeUpTo, final String sortDirection, + final String resType, + final String res, final Exception expected) { try { - APICommon.getRequestsParams(excludeUpTo, null, sortDirection, true); + APICommon.getRequestsParams(excludeUpTo, null, sortDirection, resType, res, true); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); diff --git a/src/us/kbase/test/groups/service/api/GroupsAPITest.java b/src/us/kbase/test/groups/service/api/GroupsAPITest.java index 6c93eb61..eeed6f44 100644 --- a/src/us/kbase/test/groups/service/api/GroupsAPITest.java +++ b/src/us/kbase/test/groups/service/api/GroupsAPITest.java @@ -1236,8 +1236,10 @@ public void getRequestsForGroup5() throws Exception { public void getRequestsForGroup6() throws Exception { final GetRequestsParams params = GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) - .withNullableSortAscending(false).build(); - getRequestsForGroup(null, "", "desc", params); + .withNullableSortAscending(false) + .withResource(new ResourceType("t"), new ResourceID("r")) + .build(); + getRequestsForGroup(null, "", "desc", "t", "r", params); } private void getRequestsForGroup( @@ -1246,6 +1248,17 @@ private void getRequestsForGroup( final String sortOrder, final GetRequestsParams params) throws Exception { + getRequestsForGroup(excludeUpTo, closed, sortOrder, null, null, params); + } + + private void getRequestsForGroup( + final String excludeUpTo, + final String closed, + final String sortOrder, + final String resType, + final String resource, + final GetRequestsParams params) + throws Exception { final Groups g = mock(Groups.class); final UUID id1 = UUID.randomUUID(); @@ -1275,7 +1288,7 @@ private void getRequestsForGroup( )); final List> ret = new GroupsAPI(g).getRequestsForGroup( - "t", "id", excludeUpTo, closed, sortOrder); + "t", "id", excludeUpTo, closed, sortOrder, resType, resource); assertThat("incorrect requests", ret, is(Arrays.asList( MapBuilder.newHashMap() @@ -1309,13 +1322,13 @@ private void getRequestsForGroup( public void getRequestsForGroupFailMissingInput() throws Exception { final Groups g = mock(Groups.class); - failGetRequestsForGroup(g, null, "i", null, null, + failGetRequestsForGroup(g, null, "i", null, null, null, null, new NoTokenProvidedException("No token provided")); - failGetRequestsForGroup(g, " \t ", "i", null, null, + failGetRequestsForGroup(g, " \t ", "i", null, null, null, null, new NoTokenProvidedException("No token provided")); - failGetRequestsForGroup(g, "t", null, null, null, + failGetRequestsForGroup(g, "t", null, null, null, null, null, new MissingParameterException("group id")); - failGetRequestsForGroup(g, "t", " \t ", null, null, + failGetRequestsForGroup(g, "t", " \t ", null, null, null, null, new MissingParameterException("group id")); } @@ -1323,10 +1336,16 @@ public void getRequestsForGroupFailMissingInput() throws Exception { public void getRequestsForGroupFailIllegalInput() throws Exception { final Groups g = mock(Groups.class); - failGetRequestsForGroup(g, "t", "g", " bar ", null, + failGetRequestsForGroup(g, "t", "g", " bar ", null, null, null, new IllegalParameterException("Invalid epoch ms: bar")); - failGetRequestsForGroup(g, "t", "g", "", " bat ", + failGetRequestsForGroup(g, "t", "g", "", " bat ", null, null, new IllegalParameterException("Invalid sort direction: bat")); + failGetRequestsForGroup(g, "t", "g", null, null, "t", null, + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); + failGetRequestsForGroup(g, "t", "g", null, null, null, "r", + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); } @Test @@ -1337,7 +1356,8 @@ public void getRequestsForGroupFailUnauthorized() throws Exception { GetRequestsParams.getBuilder().build())) .thenThrow(new UnauthorizedException("yay")); - failGetRequestsForGroup(g, "t", "i", null, null, new UnauthorizedException("yay")); + failGetRequestsForGroup(g, "t", "i", null, null, null, null, + new UnauthorizedException("yay")); } private void failGetRequestsForGroup( @@ -1346,9 +1366,12 @@ private void failGetRequestsForGroup( final String groupid, final String excludeUpTo, final String sortOrder, + final String resType, + final String resource, final Exception expected) { try { - new GroupsAPI(g).getRequestsForGroup(token, groupid, excludeUpTo, null, sortOrder); + new GroupsAPI(g).getRequestsForGroup( + token, groupid, excludeUpTo, null, sortOrder, resType, resource); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); diff --git a/src/us/kbase/test/groups/service/api/RequestAPITest.java b/src/us/kbase/test/groups/service/api/RequestAPITest.java index 9f67a0a4..7c379bed 100644 --- a/src/us/kbase/test/groups/service/api/RequestAPITest.java +++ b/src/us/kbase/test/groups/service/api/RequestAPITest.java @@ -49,6 +49,8 @@ import us.kbase.groups.core.request.RequestID; import us.kbase.groups.core.request.RequestType; import us.kbase.groups.core.resource.ResourceDescriptor; +import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.service.api.RequestAPI; import us.kbase.groups.service.api.RequestAPI.DenyRequestJSON; import us.kbase.test.groups.MapBuilder; @@ -384,8 +386,10 @@ public void getCreatedRequests5() throws Exception { public void getCreatedRequests6() throws Exception { final GetRequestsParams params = GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) - .withNullableSortAscending(false).build(); - getCreatedRequests(null, "", "desc", params); + .withNullableSortAscending(false) + .withResource(new ResourceType("t"), new ResourceID("r")) + .build(); + getCreatedRequests(null, "", "desc", "t", "r", params); } private void getCreatedRequests( @@ -394,13 +398,24 @@ private void getCreatedRequests( final String order, final GetRequestsParams params) throws Exception { + getCreatedRequests(excludeUpTo, closed, order, null, null, params); + } + + private void getCreatedRequests( + final String excludeUpTo, + final String closed, + final String order, + final String resType, + final String res, + final GetRequestsParams params) + throws Exception { final Groups g = mock(Groups.class); when(g.getRequestsForRequester(new Token("t"), params)) .thenReturn(Arrays.asList(REQ_DENIED, REQ_MIN, REQ_TARG)); final List> ret = new RequestAPI(g).getCreatedRequests( - "t", excludeUpTo, closed, order); + "t", excludeUpTo, closed, order, resType, res); assertThat("incorrect reqs", ret, is(Arrays.asList( REQ_DENIED_JSON, REQ_MIN_JSON, REQ_TARG_JSON))); @@ -410,9 +425,9 @@ private void getCreatedRequests( public void getCreatedRequestsMissingInput() throws Exception { final Groups g = mock(Groups.class); - failGetCreatedRequests(g, null, null, null, + failGetCreatedRequests(g, null, null, null, null, null, new NoTokenProvidedException("No token provided")); - failGetCreatedRequests(g, " \t ", null, null, + failGetCreatedRequests(g, " \t ", null, null, null, null, new NoTokenProvidedException("No token provided")); } @@ -420,10 +435,16 @@ public void getCreatedRequestsMissingInput() throws Exception { public void getCreatedRequestsIllegalInput() throws Exception { final Groups g = mock(Groups.class); - failGetCreatedRequests(g, "t", " yay ", null, + failGetCreatedRequests(g, "t", " yay ", null, null, null, new IllegalParameterException("Invalid epoch ms: yay")); - failGetCreatedRequests(g, "t", null, "boo", + failGetCreatedRequests(g, "t", null, "boo", null, null, new IllegalParameterException("Invalid sort direction: boo")); + failGetCreatedRequests(g, "t", null, null, "t", null, + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); + failGetCreatedRequests(g, "t", null, null, null, "r", + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); } @Test @@ -433,7 +454,7 @@ public void getCreatedRequestsFailInvalidToken() throws Exception { when(g.getRequestsForRequester(new Token("t"), GetRequestsParams.getBuilder().build())) .thenThrow(new InvalidTokenException()); - failGetCreatedRequests(g, "t", null, null, new InvalidTokenException()); + failGetCreatedRequests(g, "t", null, null, null, null, new InvalidTokenException()); } private void failGetCreatedRequests( @@ -441,9 +462,12 @@ private void failGetCreatedRequests( final String token, final String excludeUpTo, final String sortOrder, + final String resType, + final String res, final Exception expected) { try { - new RequestAPI(g).getCreatedRequests(token, excludeUpTo, null, sortOrder); + new RequestAPI(g).getCreatedRequests( + token, excludeUpTo, null, sortOrder, resType, res); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); @@ -492,8 +516,10 @@ public void getTargetedRequests5() throws Exception { public void getTargetedRequests6() throws Exception { final GetRequestsParams params = GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) - .withNullableSortAscending(false).build(); - getTargetedRequests(null, "", "desc", params); + .withNullableSortAscending(false) + .withResource(new ResourceType("t"), new ResourceID("r")) + .build(); + getTargetedRequests(null, "", "desc", "t", "r", params); } private void getTargetedRequests( @@ -502,13 +528,24 @@ private void getTargetedRequests( final String order, final GetRequestsParams params) throws Exception { + getTargetedRequests(excludeUpTo, closed, order, null, null, params); + } + + private void getTargetedRequests( + final String excludeUpTo, + final String closed, + final String order, + final String resType, + final String res, + final GetRequestsParams params) + throws Exception { final Groups g = mock(Groups.class); when(g.getRequestsForTarget(new Token("t"), params)) .thenReturn(Arrays.asList(REQ_MIN, REQ_DENIED, REQ_TARG)); final List> ret = new RequestAPI(g).getTargetedRequests( - "t", excludeUpTo, closed, order); + "t", excludeUpTo, closed, order, resType, res); assertThat("incorrect reqs", ret, is(Arrays.asList( REQ_MIN_JSON, REQ_DENIED_JSON, REQ_TARG_JSON))); @@ -518,9 +555,9 @@ private void getTargetedRequests( public void getTargetedRequestsMissingInput() throws Exception { final Groups g = mock(Groups.class); - failGetTargetedRequests(g, null, null, null, + failGetTargetedRequests(g, null, null, null, null, null, new NoTokenProvidedException("No token provided")); - failGetTargetedRequests(g, " \t ", null, null, + failGetTargetedRequests(g, " \t ", null, null, null, null, new NoTokenProvidedException("No token provided")); } @@ -528,10 +565,16 @@ public void getTargetedRequestsMissingInput() throws Exception { public void getTargetedRequestsIllegalInput() throws Exception { final Groups g = mock(Groups.class); - failGetTargetedRequests(g, "t", " whoo" , null, + failGetTargetedRequests(g, "t", " whoo" , null, null, null, new IllegalParameterException("Invalid epoch ms: whoo")); - failGetTargetedRequests(g, "t", null, "but mommy ", + failGetTargetedRequests(g, "t", null, "but mommy ", null, null, new IllegalParameterException("Invalid sort direction: but mommy")); + failGetTargetedRequests(g, "t", null, null, "t", null, + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); + failGetTargetedRequests(g, "t", null, null, null, "r", + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); } @Test @@ -541,7 +584,7 @@ public void getTargetedRequestsFailAuth() throws Exception { when(g.getRequestsForTarget(new Token("t"), GetRequestsParams.getBuilder().build())) .thenThrow(new AuthenticationException(ErrorType.AUTHENTICATION_FAILED, "yikes")); - failGetTargetedRequests(g, "t", null, null, new AuthenticationException( + failGetTargetedRequests(g, "t", null, null, null, null, new AuthenticationException( ErrorType.AUTHENTICATION_FAILED, "yikes")); } @@ -550,9 +593,11 @@ private void failGetTargetedRequests( final String token, final String excludeUpTo, final String order, + final String resType, + final String res, final Exception expected) { try { - new RequestAPI(g).getTargetedRequests(token, excludeUpTo, null, order); + new RequestAPI(g).getTargetedRequests(token, excludeUpTo, null, order, resType, res); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); @@ -601,8 +646,10 @@ public void getRequestsForAdministratedGroups5() throws Exception { public void getRequestsForAdministratedGroups6() throws Exception { final GetRequestsParams params = GetRequestsParams.getBuilder() .withNullableIncludeClosed(true) - .withNullableSortAscending(false).build(); - getRequestsForAdministratedGroups(null, "", "desc", params); + .withNullableSortAscending(false) + .withResource(new ResourceType("t"), new ResourceID("r")) + .build(); + getRequestsForAdministratedGroups(null, "", "desc", "t", "r", params); } private void getRequestsForAdministratedGroups( @@ -611,13 +658,24 @@ private void getRequestsForAdministratedGroups( final String order, final GetRequestsParams params) throws Exception { + getRequestsForAdministratedGroups(excludeUpTo, closed, order, null, null, params); + } + + private void getRequestsForAdministratedGroups( + final String excludeUpTo, + final String closed, + final String order, + final String resType, + final String res, + final GetRequestsParams params) + throws Exception { final Groups g = mock(Groups.class); when(g.getRequestsForGroups(new Token("t"), params)) .thenReturn(Arrays.asList(REQ_MIN, REQ_DENIED, REQ_TARG)); final List> ret = new RequestAPI(g).getRequestsForAdministratedGroups( - "t", excludeUpTo, closed, order); + "t", excludeUpTo, closed, order, resType, res); assertThat("incorrect reqs", ret, is(Arrays.asList( REQ_MIN_JSON, REQ_DENIED_JSON, REQ_TARG_JSON))); @@ -627,9 +685,9 @@ private void getRequestsForAdministratedGroups( public void getRequestsForAdministratedGroupsMissingInput() throws Exception { final Groups g = mock(Groups.class); - failGetRequestsForAdministratedGroups(g, null, null, null, + failGetRequestsForAdministratedGroups(g, null, null, null, null, null, new NoTokenProvidedException("No token provided")); - failGetRequestsForAdministratedGroups(g, " \t ", null, null, + failGetRequestsForAdministratedGroups(g, " \t ", null, null, null, null, new NoTokenProvidedException("No token provided")); } @@ -637,10 +695,16 @@ public void getRequestsForAdministratedGroupsMissingInput() throws Exception { public void getRequestsForAdministratedGroupsIllegalInput() throws Exception { final Groups g = mock(Groups.class); - failGetRequestsForAdministratedGroups(g, "t", " whoo" , null, + failGetRequestsForAdministratedGroups(g, "t", " whoo" , null, null, null, new IllegalParameterException("Invalid epoch ms: whoo")); - failGetRequestsForAdministratedGroups(g, "t", null, "but mommy ", + failGetRequestsForAdministratedGroups(g, "t", null, "but mommy ", null, null, new IllegalParameterException("Invalid sort direction: but mommy")); + failGetRequestsForAdministratedGroups(g, "t", null, null, "t", null, + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); + failGetRequestsForAdministratedGroups(g, "t", null, null, null, "r", + new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided")); } @Test @@ -650,8 +714,8 @@ public void getRequestsForAdministratedGroupsFailAuth() throws Exception { when(g.getRequestsForGroups(new Token("t"), GetRequestsParams.getBuilder().build())) .thenThrow(new AuthenticationException(ErrorType.AUTHENTICATION_FAILED, "yikes")); - failGetRequestsForAdministratedGroups(g, "t", null, null, new AuthenticationException( - ErrorType.AUTHENTICATION_FAILED, "yikes")); + failGetRequestsForAdministratedGroups(g, "t", null, null, null, null, + new AuthenticationException(ErrorType.AUTHENTICATION_FAILED, "yikes")); } private void failGetRequestsForAdministratedGroups( @@ -659,9 +723,12 @@ private void failGetRequestsForAdministratedGroups( final String token, final String excludeUpTo, final String order, + final String resType, + final String res, final Exception expected) { try { - new RequestAPI(g).getRequestsForAdministratedGroups(token, excludeUpTo, null, order); + new RequestAPI(g).getRequestsForAdministratedGroups( + token, excludeUpTo, null, order, resType, res); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); From c67d1953d124d28e833be82503dc5b27ceab75b6 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 18:09:48 -0700 Subject: [PATCH 18/39] Get groups by IDs in mongo layer --- .../kbase/groups/storage/GroupsStorage.java | 11 +- .../storage/mongo/MongoGroupsStorage.java | 17 +++ .../mongo/MongoGroupsStorageOpsTest.java | 128 ++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index 06e66230..33bea2b1 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -71,6 +71,15 @@ void updateGroup(GroupUpdateParams update, Instant modDate) */ Group getGroup(GroupID groupID) throws GroupsStorageException, NoSuchGroupException; + /** Get multiple groups. + * @param groupIDs the IDs of the groups. + * @return the groups. + * @throws NoSuchGroupException if one or more of the groups does not exist. + * @throws GroupsStorageException if an error occurs contacting the storage system. + */ + Set getGroups(Set groupIDs) + throws NoSuchGroupException, GroupsStorageException; + /** Get the name of one or more groups. * @param user an optional user to determine whether the user is a member of the group. * If no user is provided, group membership is considered to be false. @@ -89,7 +98,7 @@ List getGroupNames(UserName user, Set groupID) */ boolean getGroupExists(GroupID groupID) throws GroupsStorageException; - /** Get the groups the member belongs to, up to a maximum of 100000 groups, sorted by ID. + /** Get the groups the member belongs to, sorted by ID. * @param user the user for whom groups will be returned. * @return the user's groups. * @throws GroupsStorageException if an error occurs contacting the storage system. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 4d6770c7..863af2af 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -592,6 +592,23 @@ private Document getGroupDoc(final GroupID groupID, final Document projection) return grp; } + @Override + public Set getGroups(final Set groupIDs) + throws NoSuchGroupException, GroupsStorageException { + checkNoNullsInCollection(groupIDs, "groupIDs"); + final Document query = new Document(Fields.GROUP_ID, new Document("$in", groupIDs.stream() + .map(g -> g.getName()).collect(Collectors.toList()))); + final List retgrp = getList(COL_GROUPS, query, null, null, 0, d ->toGroup(d)); + final Set got = retgrp.stream().map(g -> g.getGroupID()) + .collect(Collectors.toSet()); + final Set missing = new HashSet<>(groupIDs); // in case groups is immutable + missing.removeAll(got); + if (!missing.isEmpty()) { + throw new NoSuchGroupException(missing.iterator().next().getName()); + } + return new HashSet<>(retgrp); + } + @Override public List getGroupNames( final UserName user, diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index ffe67c01..aa47a53b 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -257,6 +258,106 @@ null, set(new GroupID("gid"))), .build()))); } + @Test + public void getGroups() throws Exception { + //minimal + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .build()); + + // also minimal + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid21"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .build()); + + //maximal + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid6"), new GroupName("name"), + GroupUser.getBuilder(new UserName("uname"), inst(21000)) + .withCustomField(new NumberedCustomField("f"), "val") + .withNullableLastVisit(inst(42000)) + .build(), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .withIsPrivate(true) + .withPrivateMemberList(false) + .withMember(GroupUser.getBuilder(new UserName("foo"), inst(40000)) + .withNullableLastVisit(inst(76000)) + .withCustomField(new NumberedCustomField("field"), "value") + .build()) + .withMember(toGUser("bar")) + .withAdministrator(toGUser("a1")) + .withAdministrator(GroupUser.getBuilder(new UserName("a3"), inst(50000)) + .withCustomField(new NumberedCustomField("whee"), "whoo") + .withCustomField(new NumberedCustomField("droogies"), "hihihi") + .withNullableLastVisit(inst(78000)) + .build()) + .withResource(new ResourceType("t"), new ResourceDescriptor(new ResourceID("r"))) + .withResource(new ResourceType("t"), new ResourceDescriptor( + new ResourceAdministrativeID("a"), + new ResourceID("b")), + inst(76000)) + .withResource(new ResourceType("x"), new ResourceDescriptor(new ResourceID("y")), + inst(34000)) + .withResource(new ResourceType("x"), new ResourceDescriptor( + new ResourceAdministrativeID("b"), + new ResourceID("z"))) + .withCustomField(new NumberedCustomField("foo-83"), "bar") + .withCustomField(new NumberedCustomField("whoo"), "whee") + .build()); + + assertThat("incorrect groups", manager.storage.getGroups( + set(new GroupID("gid"), new GroupID("gid21"), new GroupID("gid6"))), + is(new HashSet<>(Arrays.asList( + Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(inst(20000), inst(30000))) + .build(), + Group.getBuilder( + new GroupID("gid21"), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(inst(20000), inst(30000))) + .build(), + Group.getBuilder( + new GroupID("gid6"), new GroupName("name"), + GroupUser.getBuilder(new UserName("uname"), inst(21000)) + .withCustomField(new NumberedCustomField("f"), "val") + .withNullableLastVisit(inst(42000)) + .build(), + new CreateAndModTimes(inst(20000), inst(30000))) + .withIsPrivate(true) + .withPrivateMemberList(false) + .withMember(GroupUser.getBuilder(new UserName("foo"), inst(40000)) + .withNullableLastVisit(inst(76000)) + .withCustomField(new NumberedCustomField("field"), "value") + .build()) + .withMember(toGUser("bar")) + .withAdministrator(toGUser("a1")) + .withAdministrator(GroupUser.getBuilder( + new UserName("a3"), inst(50000)) + .withCustomField(new NumberedCustomField("whee"), "whoo") + .withCustomField( + new NumberedCustomField("droogies"), "hihihi") + .withNullableLastVisit(inst(78000)) + .build()) + .withResource(new ResourceType("t"), + new ResourceDescriptor(new ResourceID("r"))) + .withResource(new ResourceType("t"), new ResourceDescriptor( + new ResourceAdministrativeID("a"), + new ResourceID("b")), + inst(76000)) + .withResource(new ResourceType("x"), + new ResourceDescriptor(new ResourceID("y")), + inst(34000)) + .withResource(new ResourceType("x"), new ResourceDescriptor( + new ResourceAdministrativeID("b"), + new ResourceID("z"))) + .withCustomField(new NumberedCustomField("foo-83"), "bar") + .withCustomField(new NumberedCustomField("whoo"), "whee") + .build() + )))); + } + @Test public void getGroupWithDefaultPrivateMemberList() throws Exception { manager.storage.createGroup(Group.getBuilder( @@ -414,6 +515,33 @@ public void getGroupExistsFail() throws Exception { TestCommon.assertExceptionCorrect(got, new NullPointerException("groupID")); } } + + @Test + public void getGroupsByIDsFail() throws Exception { + getGroupsFail(null, new NullPointerException("groupIDs")); + getGroupsFail(set(new GroupID("i"), null), new NullPointerException( + "Null item in collection groupIDs")); + + final Set ids = new HashSet<>(); + for (int i = 1; i < 5; i++) { + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid" + i), new GroupName("name"), toGUser("uname"), + new CreateAndModTimes(Instant.ofEpochMilli(20000), Instant.ofEpochMilli(30000))) + .build()); + ids.add(new GroupID("gid" + i)); + } + ids.add(new GroupID("gid6")); + getGroupsFail(ids, new NoSuchGroupException("gid6")); + } + + private void getGroupsFail(final Set ids, final Exception expected) { + try { + manager.storage.getGroups(ids); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } @Test public void illegalGroupDataInDB() throws Exception { From fa78db95ec1b1a77d3a3f867d250ceaa1859e18b Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 19:39:05 -0700 Subject: [PATCH 19/39] Add core code for listing groups by specified IDs --- README.md | 6 +- src/us/kbase/groups/core/Groups.java | 51 ++- .../kbase/groups/storage/GroupsStorage.java | 3 +- .../storage/mongo/MongoGroupsStorage.java | 3 +- src/us/kbase/test/groups/core/GroupsTest.java | 301 +++++++++++++----- 5 files changed, 263 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 29623f8c..874bc341 100644 --- a/README.md +++ b/README.md @@ -246,8 +246,8 @@ AUTHORIZATION OPTIONAL GET /group[?excludeupto=&order=] RETURNS: -A list of Groups. Only the id, name, owner, role, memcount, rescount, custom, lastvisit, -createdate, and moddate fields are included. +A list of Groups. Only the id, private, name, owner, role, memcount, rescount, custom, +lastvisit, createdate, and moddate fields are included. ``` The owner field consists only of the user name for this endpoint. For most other endpoints, @@ -538,7 +538,7 @@ Possible actions are `Cancel`, `Accept`, and `Deny`. AUTHORIZATION REQUIRED GET /request/id//group -A Group. Only the id, name, owner, role, memcount, rescount, custom, lastvisit, +A Group. Only the id, name, private, owner, role, memcount, rescount, custom, lastvisit, createdate, and moddate fields are included. ``` diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index fe49f31d..2120d1e5 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -71,6 +71,7 @@ public class Groups { private static final Duration REQUEST_EXPIRE_TIME = Duration.of(14, ChronoUnit.DAYS); private static final int MAX_GROUP_NAMES_RETURNED = 1000; private static final int MAX_GROUP_HAS_REQUESTS_COUNT = 100; + private static final int MAX_GROUP_LIST_COUNT = 100; private final GroupsStorage storage; private final UserHandler userHandler; private final Map resourceHandlers; @@ -545,15 +546,47 @@ public List getGroups(final Token userToken, final GetGroupsParams pa checkNotNull(params, "params"); final UserName user = getOptionalUser(userToken); return storage.getGroups(params, user).stream() - .map(g -> GroupView.getBuilder(g, user) - // this seems odd. Maybe there's a better way to deal with this? - .withMinimalViewFieldDeterminer( - f -> validators.getConfigOrEmpty(f.getFieldRoot()) - .map(c -> c.isMinimalViewField()).orElse(false)) - .withPublicFieldDeterminer( - f -> validators.getConfigOrEmpty(f.getFieldRoot()) - .map(c -> c.isPublicField()).orElse(false)) - .build()) + .map(g -> toMinimalView(user, g)).collect(Collectors.toList()); + } + + private GroupView toMinimalView(final UserName user, final Group g) { + return GroupView.getBuilder(g, user) + // this seems odd. Maybe there's a better way to deal with this? + .withMinimalViewFieldDeterminer( + f -> validators.getConfigOrEmpty(f.getFieldRoot()) + .map(c -> c.isMinimalViewField()).orElse(false)) + .withPublicFieldDeterminer( + f -> validators.getConfigOrEmpty(f.getFieldRoot()) + .map(c -> c.isPublicField()).orElse(false)) + .build(); + } + + /** Get a set of specified groups. At most 100 groups may be specified. + * @param userToken the user's token. + * @param groupIDs the IDs of the group to fetch. + * @return the groups, listed in the same order as the input IDs. + * @throws GroupsStorageException if an error occurs contacting the storage system. + * @throws InvalidTokenException if the token is invalid. + * @throws AuthenticationException if authentication fails. + * @throws NoSuchGroupException if there is no corresponding group for one of the IDs. + * @throws IllegalParameterException if more than 100 group IDs are submitted. + */ + public List getGroups(final Token userToken, final List groupIDs) + throws InvalidTokenException, AuthenticationException, NoSuchGroupException, + GroupsStorageException, IllegalParameterException { + checkNoNullsInCollection(groupIDs, "groupIDs"); + if (groupIDs.isEmpty()) { + return Collections.emptyList(); + } + if (groupIDs.size() > MAX_GROUP_LIST_COUNT) { + throw new IllegalParameterException(String.format( + "No more than %s group IDs may be specified", MAX_GROUP_LIST_COUNT)); + } + final UserName user = getOptionalUser(userToken); + final Set groups = storage.getGroups(groupIDs); + final Map idToGroup = groups.stream() + .collect(Collectors.toMap(g -> g.getGroupID(), g -> g)); + return groupIDs.stream().map(gid -> toMinimalView(user, idToGroup.get(gid))) .collect(Collectors.toList()); } diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index 33bea2b1..b378f028 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -1,6 +1,7 @@ package us.kbase.groups.storage; import java.time.Instant; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -77,7 +78,7 @@ void updateGroup(GroupUpdateParams update, Instant modDate) * @throws NoSuchGroupException if one or more of the groups does not exist. * @throws GroupsStorageException if an error occurs contacting the storage system. */ - Set getGroups(Set groupIDs) + Set getGroups(Collection groupIDs) throws NoSuchGroupException, GroupsStorageException; /** Get the name of one or more groups. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 863af2af..4cd3e94c 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -10,6 +10,7 @@ import java.time.Clock; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -593,7 +594,7 @@ private Document getGroupDoc(final GroupID groupID, final Document projection) } @Override - public Set getGroups(final Set groupIDs) + public Set getGroups(final Collection groupIDs) throws NoSuchGroupException, GroupsStorageException { checkNoNullsInCollection(groupIDs, "groupIDs"); final Document query = new Document(Fields.GROUP_ID, new Document("$in", groupIDs.stream() diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 2e506224..628da9fa 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -21,6 +21,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -1556,12 +1558,12 @@ public void getGroups() throws Exception { null)) .thenReturn(Arrays.asList( Group.getBuilder( - new GroupID("id1"), new GroupName("name1"), toGUser("u1"), - new CreateAndModTimes( - Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) - .withMember(toGUser("m1")) - .withAdministrator(toGUser("a1")) - .build(), + new GroupID("id1"), new GroupName("name1"), toGUser("u1"), + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withMember(toGUser("m1")) + .withAdministrator(toGUser("a1")) + .build(), Group.getBuilder( new GroupID("id2"), new GroupName("name2"), toGUser("u2"), new CreateAndModTimes(Instant.ofEpochMilli(10000))) @@ -1575,19 +1577,19 @@ public void getGroups() throws Exception { .build()), is(Arrays.asList( GroupView.getBuilder(Group.getBuilder( - new GroupID("id1"), new GroupName("name1"), toGUser("u1"), - new CreateAndModTimes( - Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) - .withMember(toGUser("m1")) - .withAdministrator(toGUser("a1")) - .build(), - null) + new GroupID("id1"), new GroupName("name1"), toGUser("u1"), + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withMember(toGUser("fakename")) + .withAdministrator(toGUser("fakename2")) + .build(), + null) .build(), GroupView.getBuilder(Group.getBuilder( new GroupID("id2"), new GroupName("name2"), toGUser("u2"), new CreateAndModTimes(Instant.ofEpochMilli(10000))) - .withMember(toGUser("whee")) - .withAdministrator(toGUser("whoo")) + .withMember(toGUser("fakename")) + .withAdministrator(toGUser("fakename2")) .build(), null) .build()) )); @@ -1607,6 +1609,7 @@ private GroupUser gGWCFWithCF(final GroupUser.Builder b) throws Exception { @Test public void getGroupsWithCustomFields() throws Exception { + // tests both getGroups methods that return minimal views // tests that the custom fields are displayed correctly // note user custom fields are never displayed in list view final TestMocks mocks = initTestMocks(); @@ -1615,24 +1618,25 @@ public void getGroupsWithCustomFields() throws Exception { when(mocks.userHandler.getUser(new Token("m1"))).thenReturn(new UserName("m1")); when(mocks.userHandler.getUser(new Token("m2"))).thenReturn(new UserName("m2")); - when(mocks.storage.getGroups(eq(mtparams), any())) - .thenReturn(Arrays.asList(Group.getBuilder( - new GroupID("id1"), new GroupName("name1"), - gGWCFWithCF(GroupUser.getBuilder(new UserName("o1"), inst(10000))), - new CreateAndModTimes( - Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) - .withMember(gGWCFWithCF(GroupUser.getBuilder(new UserName("m1"), - inst(20000)))) - .withAdministrator(gGWCFWithCF(GroupUser.getBuilder(new UserName("a1"), - inst(30000)))) - .withCustomField(new NumberedCustomField("minpub-6"), "minpub") - .withCustomField(new NumberedCustomField("minpriv-7"), "minpriv") - .withCustomField(new NumberedCustomField("pub-8"), "pub") - .withCustomField(new NumberedCustomField("priv-9"), "priv") - // mockito returns Optional.empty() by default, so no need to mock - .withCustomField(new NumberedCustomField("missingmin"), "missingonmin") - .withCustomField(new NumberedCustomField("missingpub"), "missingonpub") - .build())); + final Group grp = Group.getBuilder( + new GroupID("id1"), new GroupName("name1"), + gGWCFWithCF(GroupUser.getBuilder(new UserName("o1"), inst(10000))), + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withMember(gGWCFWithCF(GroupUser.getBuilder(new UserName("m1"), + inst(20000)))) + .withAdministrator(gGWCFWithCF(GroupUser.getBuilder(new UserName("a1"), + inst(30000)))) + .withCustomField(new NumberedCustomField("minpub-6"), "minpub") + .withCustomField(new NumberedCustomField("minpriv-7"), "minpriv") + .withCustomField(new NumberedCustomField("pub-8"), "pub") + .withCustomField(new NumberedCustomField("priv-9"), "priv") + // mockito returns Optional.empty() by default, so no need to mock + .withCustomField(new NumberedCustomField("missingmin"), "missingonmin") + .withCustomField(new NumberedCustomField("missingpub"), "missingonpub") + .build(); + when(mocks.storage.getGroups(eq(mtparams), any())).thenReturn(Arrays.asList(grp)); + when(mocks.storage.getGroups(Arrays.asList(new GroupID("id1")))).thenReturn(set(grp)); when(mocks.validators.getConfigOrEmpty(new CustomField("minpub"))).thenReturn( Optional.of(FieldConfiguration.getBuilder() @@ -1670,70 +1674,80 @@ public void getGroupsWithCustomFields() throws Exception { .withNullableIsMinimalViewField(true) .build())); - - // null user - assertThat("incorrect groups", mocks.groups.getGroups(null, mtparams), - is(Arrays.asList(GroupView.getBuilder(Group.getBuilder( - new GroupID("id1"), new GroupName("name1"), - GroupUser.getBuilder(new UserName("o1"), inst(10000)) - .build(), - new CreateAndModTimes( - Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) - .withCustomField(new NumberedCustomField("minpub-6"), "minpub") - // these are basically dummies to test the is member logic in group view - .withMember(toGUser("m1")) - .withAdministrator(toGUser("a1")) + final GroupView expected1 = GroupView.getBuilder(Group.getBuilder( + new GroupID("id1"), new GroupName("name1"), + GroupUser.getBuilder(new UserName("o1"), inst(10000)) .build(), - null) - .withMinimalViewFieldDeterminer(f -> true) - .withPublicFieldDeterminer(f -> true) - .withPublicUserFieldDeterminer(f -> true) - .build()))); + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withCustomField(new NumberedCustomField("minpub-6"), "minpub") + // these are basically dummies to test the is member logic in group view + .withMember(toGUser("m1")) + .withAdministrator(toGUser("a1")) + .build(), + null) + .withMinimalViewFieldDeterminer(f -> true) + .withPublicFieldDeterminer(f -> true) + .withPublicUserFieldDeterminer(f -> true) + .build(); + assertThat("incorrect groups", mocks.groups.getGroups(null, mtparams), + is(Arrays.asList(expected1))); + assertThat("incorrect groups", + mocks.groups.getGroups(null, Arrays.asList(new GroupID("id1"))), + is(Arrays.asList(expected1))); // non member - assertThat("incorrect groups", mocks.groups.getGroups(new Token("m2"), mtparams), - is(Arrays.asList(GroupView.getBuilder(Group.getBuilder( - new GroupID("id1"), new GroupName("name1"), - GroupUser.getBuilder(new UserName("o1"), inst(10000)) - .build(), - new CreateAndModTimes( - Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) - .withCustomField(new NumberedCustomField("minpub-6"), "minpub") - // these are basically dummies to test the is member logic in group view - .withMember(toGUser("m1")) - .withAdministrator(toGUser("a1")) + final GroupView expected2 = GroupView.getBuilder(Group.getBuilder( + new GroupID("id1"), new GroupName("name1"), + GroupUser.getBuilder(new UserName("o1"), inst(10000)) .build(), - new UserName("m2")) - .withMinimalViewFieldDeterminer(f -> true) - .withPublicFieldDeterminer(f -> true) - .withPublicUserFieldDeterminer(f -> true) - .build()))); + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withCustomField(new NumberedCustomField("minpub-6"), "minpub") + // these are basically dummies to test the is member logic in group view + .withMember(toGUser("m1")) + .withAdministrator(toGUser("a1")) + .build(), + new UserName("m2")) + .withMinimalViewFieldDeterminer(f -> true) + .withPublicFieldDeterminer(f -> true) + .withPublicUserFieldDeterminer(f -> true) + .build(); + assertThat("incorrect groups", mocks.groups.getGroups(new Token("m2"), mtparams), + is(Arrays.asList(expected2))); + assertThat("incorrect groups", + mocks.groups.getGroups(new Token("m2"), Arrays.asList(new GroupID("id1"))), + is(Arrays.asList(expected1))); //member - assertThat("incorrect groups", mocks.groups.getGroups(new Token("m1"), mtparams), - is(Arrays.asList(GroupView.getBuilder(Group.getBuilder( - new GroupID("id1"), new GroupName("name1"), - GroupUser.getBuilder(new UserName("o1"), inst(10000)) - .build(), - new CreateAndModTimes( - Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) - .withCustomField(new NumberedCustomField("minpub-6"), "minpub") - .withCustomField(new NumberedCustomField("minpriv-7"), "minpriv") - .withCustomField(new NumberedCustomField("missingpub"), "missingonpub") - // these are basically dummies to test the is member logic in group view - .withMember(toGUser("m1")) - .withAdministrator(toGUser("a1")) + final GroupView expected3 = GroupView.getBuilder(Group.getBuilder( + new GroupID("id1"), new GroupName("name1"), + GroupUser.getBuilder(new UserName("o1"), inst(10000)) .build(), - new UserName("m1")) - .withMinimalViewFieldDeterminer(f -> true) - .withPublicFieldDeterminer(f -> true) - .withPublicUserFieldDeterminer(f -> true) - .build()))); + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withCustomField(new NumberedCustomField("minpub-6"), "minpub") + .withCustomField(new NumberedCustomField("minpriv-7"), "minpriv") + .withCustomField(new NumberedCustomField("missingpub"), "missingonpub") + // these are basically dummies to test the is member logic in group view + .withMember(toGUser("m1")) + .withAdministrator(toGUser("a1")) + .build(), + new UserName("m1")) + .withMinimalViewFieldDeterminer(f -> true) + .withPublicFieldDeterminer(f -> true) + .withPublicUserFieldDeterminer(f -> true) + .build(); + assertThat("incorrect groups", mocks.groups.getGroups(new Token("m1"), mtparams), + is(Arrays.asList(expected3))); + assertThat("incorrect groups", + mocks.groups.getGroups(new Token("m1"), Arrays.asList(new GroupID("id1"))), + is(Arrays.asList(expected3))); } @Test - public void getGroupsWithUser() throws Exception { + public void getGroupsWithPrivateGroup() throws Exception { final TestMocks mocks = initTestMocks(); final GetGroupsParams ggp = GetGroupsParams.getBuilder() @@ -1773,13 +1787,126 @@ public void getGroupsWithUser() throws Exception { @Test public void getGroupsFail() throws Exception { try { - initTestMocks().groups.getGroups(null, null); + initTestMocks().groups.getGroups(null, (GetGroupsParams) null); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, new NullPointerException("params")); } } + @Test + public void getGroupsByIDsEmpty() throws Exception { + assertThat("incorrect groups", + initTestMocks().groups.getGroups(null, Collections.emptyList()), + is(Collections.emptyList())); + } + + @Test + public void getGroupsByIDs() throws Exception { + // tests that private groups have a private view and are included, unlike the list + final TestMocks mocks = initTestMocks(); + when(mocks.storage.getGroups(Arrays.asList( + new GroupID("id1"), new GroupID("id2"), new GroupID("id4"), new GroupID("id3"), + new GroupID("id4")))) + .thenReturn(set( + Group.getBuilder( + new GroupID("id1"), new GroupName("name1"), toGUser("u1"), + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withMember(toGUser("m1")) + .withAdministrator(toGUser("a1")) + .build(), + Group.getBuilder( + new GroupID("id2"), new GroupName("name2"), toGUser("u2"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withIsPrivate(true) + .withMember(toGUser("whee")) + .withAdministrator(toGUser("whoo")) + .build(), + Group.getBuilder( + new GroupID("id3"), new GroupName("name3"), toGUser("u3"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("bar")) + .withAdministrator(toGUser("baz")) + .build(), + Group.getBuilder( + new GroupID("id4"), new GroupName("name4"), toGUser("u4"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("boop")) + .withAdministrator(toGUser("poob")) + .build() + )); + + assertThat("incorrect groups", mocks.groups.getGroups(null, Arrays.asList( + new GroupID("id1"), new GroupID("id2"), new GroupID("id4"), new GroupID("id3"), + new GroupID("id4"))), + is(Arrays.asList( + GroupView.getBuilder(Group.getBuilder( + new GroupID("id1"), new GroupName("name1"), toGUser("u1"), + new CreateAndModTimes( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000))) + .withMember(toGUser("fakename")) + .withAdministrator(toGUser("fakename2")) + .build(), + null) + .build(), + GroupView.getBuilder(Group.getBuilder( + new GroupID("id2"), new GroupName("fake"), toGUser("fake"), + new CreateAndModTimes(Instant.ofEpochMilli(50000))) + .withIsPrivate(true) + .build(), + null) + .build(), + GroupView.getBuilder(Group.getBuilder( + new GroupID("id4"), new GroupName("name4"), toGUser("u4"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("fakename")) + .withAdministrator(toGUser("fakename2")) + .build(), + null) + .build(), + GroupView.getBuilder(Group.getBuilder( + new GroupID("id3"), new GroupName("name3"), toGUser("u3"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("fakename")) + .withAdministrator(toGUser("fakename2")) + .build(), + null) + .build(), + GroupView.getBuilder(Group.getBuilder( + new GroupID("id4"), new GroupName("name4"), toGUser("u4"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("fakename")) + .withAdministrator(toGUser("fakename2")) + .build(), + null) + .build() + ))); + } + + @Test + public void getGroupsByIDsFailBadArgs() throws Exception { + getGroupsByIDsFail(null, new NullPointerException("groupIDs")); + getGroupsByIDsFail(Arrays.asList(new GroupID("id"), null), new NullPointerException( + "Null item in collection groupIDs")); + final List hundy = new LinkedList<>(); + for (int i = 1; i < 102; i++) { + hundy.add(new GroupID("a" + i)); + } + getGroupsByIDsFail(hundy, new IllegalParameterException( + "No more than 100 group IDs may be specified")); + } + + private void getGroupsByIDsFail(final List ids, final Exception expected) { + try { + initTestMocks().groups.getGroups(null, ids); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + + } + @Test public void requestGroupMembership() throws Exception { final TestMocks mocks = initTestMocks(); From fd9f47e0d535698d523b9a45e9eae01cd98d7dd5 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 13 Mar 2019 19:46:48 -0700 Subject: [PATCH 20/39] Tiny simplification Don't create group views for the same group twice --- src/us/kbase/groups/core/Groups.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index 2120d1e5..e6980e8c 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -584,10 +584,9 @@ public List getGroups(final Token userToken, final List grou } final UserName user = getOptionalUser(userToken); final Set groups = storage.getGroups(groupIDs); - final Map idToGroup = groups.stream() - .collect(Collectors.toMap(g -> g.getGroupID(), g -> g)); - return groupIDs.stream().map(gid -> toMinimalView(user, idToGroup.get(gid))) - .collect(Collectors.toList()); + final Map idToGroup = groups.stream() + .collect(Collectors.toMap(g -> g.getGroupID(), g -> toMinimalView(user, g))); + return groupIDs.stream().map(gid -> idToGroup.get(gid)).collect(Collectors.toList()); } /** Request membership in a group. From 5f50182fd6b9164d353b4cd630529a6354924a7f Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 12:16:27 -0700 Subject: [PATCH 21/39] Get groups by ID --- README.md | 16 +++- src/us/kbase/groups/service/api/Fields.java | 2 + .../kbase/groups/service/api/GroupsAPI.java | 40 +++++++-- .../groups/service/api/GroupsAPITest.java | 84 ++++++++++++++++--- 4 files changed, 123 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 874bc341..f6ee01b9 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ RETURNS: ``` AUTHORIZATION OPTIONAL -GET /group[?excludeupto=&order=] +GET /group[?excludeupto=&order=&groupids=] RETURNS: A list of Groups. Only the id, private, name, owner, role, memcount, rescount, custom, @@ -264,6 +264,15 @@ The query parameters are all optional: depending on the sort order. `asc` and `desc` sorts will include groups with group IDs, respectively, after and before the `excludeupto` string, non-inclusive. This can be used to page through the groups if needed. +* `groupids` - list specific groups by ID in a comma separated list + (e.g. `?groupids=groupid1,groupid2,...,groupidN`). If this parameter is specified, + all other parameters are ignored. This method of listing groups is much faster than getting + each group from the `/group/` endpoint as external resource servers are not contacted + for data. The order of the returned list is as the order of the IDs. + If an ID is listed more than once, the group data will also be listed more than once at the + same locations. At most 100 IDs may be included. Unlike the standard list, if a private group + where the user is not a member is specified, it *will* be returned, but only the `id`, + `private`, and `role` fields will be included. Whitespace between commas is ignored. If the user is anonymous or not a member of the group, only custom fields that are both public and group listable (see custom fields below) are included. If the user is a member of the group, @@ -365,6 +374,11 @@ GET /group/ RETURNS: A Group. ``` +This endpoint is fairly expensive as it causes the Groups server to contact external +resource servers for data, potentially many times depending on the amount of external resource +data in the group and the API of the resource server. See the `/group` endpoint for +cheaper options. + If the user is not a member of the group or no authorization is provided and the group is private, only the `groupid`, `private`, `role`, and `resources` fields are included. Only resources the user user administrates will be available in `resources`. diff --git a/src/us/kbase/groups/service/api/Fields.java b/src/us/kbase/groups/service/api/Fields.java index 9169d016..b5716561 100644 --- a/src/us/kbase/groups/service/api/Fields.java +++ b/src/us/kbase/groups/service/api/Fields.java @@ -107,6 +107,8 @@ public class Fields { public static final String GET_GROUPS_EXCLUDE_UP_TO = "excludeupto"; /** Set the sort order. */ public static final String GET_GROUPS_SORT_ORDER = "order"; + /** Determine which groups to list. */ + public static final String GET_GROUPS_IDS = "groupids"; /* *********************** * request listing fields diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index 83292085..f3632ae2 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -9,6 +9,7 @@ import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -33,10 +34,12 @@ import us.kbase.groups.core.GroupID; import us.kbase.groups.core.GroupName; import us.kbase.groups.core.GroupUpdateParams; +import us.kbase.groups.core.GroupView; import us.kbase.groups.core.Groups; import us.kbase.groups.core.OptionalGroupFields; import us.kbase.groups.core.OptionalGroupFields.Builder; import us.kbase.groups.core.OptionalString; +import us.kbase.groups.core.Token; import us.kbase.groups.core.UserName; import us.kbase.groups.core.exceptions.AuthenticationException; import us.kbase.groups.core.exceptions.GroupExistsException; @@ -80,15 +83,40 @@ public GroupsAPI(final Groups groups) { public List> getGroups( @HeaderParam(HEADER_TOKEN) final String token, @QueryParam(Fields.GET_GROUPS_EXCLUDE_UP_TO) final String excludeUpTo, - @QueryParam(Fields.GET_GROUPS_SORT_ORDER) final String order) + @QueryParam(Fields.GET_GROUPS_SORT_ORDER) final String order, + @QueryParam(Fields.GET_GROUPS_IDS) final String groupIDs) throws GroupsStorageException, IllegalParameterException, NoTokenProvidedException, - InvalidTokenException, AuthenticationException { - return groups.getGroups( - getToken(token, false), - getGroupsParams(excludeUpTo, order, true)) - .stream().map(g -> toGroupJSON(g)).collect(Collectors.toList()); + InvalidTokenException, AuthenticationException, NoSuchGroupException { + final List gids = getGroupIDs(groupIDs); + final Token t = getToken(token, false); + final List grps; + if (!gids.isEmpty()) { + grps = groups.getGroups(t, gids); + } else { + grps = groups.getGroups(t, getGroupsParams(excludeUpTo, order, true)); + } + return grps.stream().map(g -> toGroupJSON(g)).collect(Collectors.toList()); } + private List getGroupIDs(final String groupIDsCommaSepList) + throws IllegalParameterException { + final List ret = new LinkedList<>(); + if (groupIDsCommaSepList == null) { + return ret; + } + final String[] gids = groupIDsCommaSepList.split(","); + for (final String g: gids) { + if (!g.trim().isEmpty()) { + try { + ret.add(new GroupID(g.trim())); + } catch (MissingParameterException e) { + throw new RuntimeException("This should be impossible", e); + } + } + } + return ret; + } + private static Map getCustomFieldsAndTypeCheck( final Object customFields, final String fieldName) diff --git a/src/us/kbase/test/groups/service/api/GroupsAPITest.java b/src/us/kbase/test/groups/service/api/GroupsAPITest.java index eeed6f44..e0985e72 100644 --- a/src/us/kbase/test/groups/service/api/GroupsAPITest.java +++ b/src/us/kbase/test/groups/service/api/GroupsAPITest.java @@ -267,32 +267,36 @@ private static Builder getGroupMaxBuilder() @Test public void getGroupsNulls() throws Exception { - getGroups(null, null, null, null, GetGroupsParams.getBuilder().build()); + getGroups(null, null, null, null, null, GetGroupsParams.getBuilder().build()); } @Test public void getGroupsWhitespace() throws Exception { - getGroups(" \t ", " \t ", " \t ", null, GetGroupsParams.getBuilder().build()); + getGroups(" \t ", " \t ", " \t ", " \t ", null, + GetGroupsParams.getBuilder().build()); } @Test public void getGroupsWhitespaceValuesAsc() throws Exception { - getGroups(" tok \t ", " foo \t ", " asc \t ", new Token(" tok \t "), + getGroups(" tok \t ", " foo \t ", " asc \t ", " , \t , ", + new Token(" tok \t "), GetGroupsParams.getBuilder() .withNullableExcludeUpTo("foo").build()); } @Test public void getGroupsWhitespaceValuesDesc() throws Exception { - getGroups("t", " foo \t ", " desc \t ", new Token("t"), GetGroupsParams.getBuilder() - .withNullableExcludeUpTo("foo") - .withNullableSortAscending(false).build()); + getGroups("t", " foo \t ", " desc \t ", ",", new Token("t"), + GetGroupsParams.getBuilder() + .withNullableExcludeUpTo("foo") + .withNullableSortAscending(false).build()); } private void getGroups( final String token, final String excludeUpTo, final String order, + final String ids, // this must be null or contain ws (with commas) final Token expectedToken, final GetGroupsParams expected) throws Exception { @@ -305,21 +309,76 @@ private void getGroups( .withPublicUserFieldDeterminer(f -> f.getField().equals("something")) .build())); final List> ret = new GroupsAPI(g) - .getGroups(token, excludeUpTo, order); + .getGroups(token, excludeUpTo, order, ids); assertThat("incorrect groups", ret, is(Arrays.asList(GROUP_MAX_JSON_MIN, GROUP_MIN_JSON_MIN))); } @Test - public void failGetGroups() throws Exception { + public void getGroupsWithIDs() throws Exception { + getGroupsWithIDs(null, null); + getGroupsWithIDs(" \t ", null); + getGroupsWithIDs("t", new Token("t")); + } + + private void getGroupsWithIDs( + final String token, + final Token expectedToken) + throws Exception { + final Groups g = mock(Groups.class); + when(g.getGroups(expectedToken, + Arrays.asList(new GroupID("id2"), new GroupID("priv"), new GroupID("id")))) + .thenReturn(Arrays.asList( + GroupView.getBuilder(GROUP_MAX, new UserName("u2")) + .withMinimalViewFieldDeterminer( + f -> f.getField().equals("field-1")) + .build(), + GroupView.getBuilder(Group.getBuilder( + new GroupID("priv"), new GroupName("fake"), + GroupUser.getBuilder(new UserName("u2"), inst(20000)).build(), + new CreateAndModTimes(inst(1000))) + .withIsPrivate(true) + .build(), + new UserName("nonmember")) + .build(), + GroupView.getBuilder(GROUP_MIN, new UserName("u2")) + .withPublicUserFieldDeterminer( + f -> f.getField().equals("something")) + .build())); + + final List> ret = new GroupsAPI(g) + .getGroups(token, "id", "asc", "id2 , priv, id "); + + assertThat("incorrect groups", ret, + is(Arrays.asList( + GROUP_MAX_JSON_MIN, + ImmutableMap.of("private", true, "role", "None", "id", "priv"), + GROUP_MIN_JSON_MIN))); + } + + @Test + public void getGroupsFailBadArgs() throws Exception { final Groups g = mock(Groups.class); + + failGetGroups(g, "t", null, " asd ", null, new IllegalParameterException( + "Invalid sort direction: asd")); + failGetGroups(g, "t", null, null, " id1 , id*bad", new IllegalParameterException( + ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id id*bad: *")); + } + + private void failGetGroups( + final Groups g, + final String token, + final String excludeUpTo, + final String order, + final String ids, + final Exception expected) { try { - new GroupsAPI(g).getGroups("t", null, " asd "); + new GroupsAPI(g).getGroups(token, excludeUpTo, order, ids); fail("expected exception"); } catch (Exception got) { - TestCommon.assertExceptionCorrect(got, new IllegalParameterException( - "Invalid sort direction: asd")); + TestCommon.assertExceptionCorrect(got, expected); } } @@ -863,7 +922,8 @@ public void getGroupWithResources() throws Exception { when(g.getGroups(new Token("toke2"), GetGroupsParams.getBuilder().build())) .thenReturn(Arrays.asList(gv.withStandardView(false).build())); - final Map retmin = new GroupsAPI(g).getGroups("toke2", null, null).get(0); + final Map retmin = new GroupsAPI(g).getGroups("toke2", null, null, null) + .get(0); final Map expectedmin = new HashMap<>(); expectedmin.putAll(GROUP_MAX_JSON_MIN); expectedmin.put("role", "Admin"); From 0904f3c0879d6566921e94d49942189b45f862b4 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 14:33:32 -0700 Subject: [PATCH 22/39] Add role to GetGroupParams And a method to get a role from the role's representation. Will be needed for translating in the API layer. Also minor doc fixes. --- src/us/kbase/groups/core/GetGroupsParams.java | 45 ++++++++++++++++--- src/us/kbase/groups/core/Group.java | 19 ++++++++ .../groups/core/request/GroupRequest.java | 2 +- .../test/groups/core/GetGroupsParamsTest.java | 20 +++++++++ src/us/kbase/test/groups/core/GroupTest.java | 25 +++++++++++ 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/us/kbase/groups/core/GetGroupsParams.java b/src/us/kbase/groups/core/GetGroupsParams.java index 20847a6f..c8006e2b 100644 --- a/src/us/kbase/groups/core/GetGroupsParams.java +++ b/src/us/kbase/groups/core/GetGroupsParams.java @@ -1,9 +1,12 @@ package us.kbase.groups.core; +import static java.util.Objects.requireNonNull; import static us.kbase.groups.util.Util.isNullOrEmpty; import java.util.Optional; +import us.kbase.groups.core.Group.Role; + /** Parameters for getting a list of groups. * @author gaprice@lbl.gov * @@ -12,10 +15,15 @@ public class GetGroupsParams { private final boolean sortAscending; private final Optional excludeUpTo; + private final Role role; - private GetGroupsParams(final boolean sortAscending, final Optional excludeUpTo) { + private GetGroupsParams( + final boolean sortAscending, + final Optional excludeUpTo, + final Role role) { this.sortAscending = sortAscending; this.excludeUpTo = excludeUpTo; + this.role = role; } /** Get whether the list should be sorted in ascending or descending order. @@ -25,20 +33,28 @@ public boolean isSortAscending() { return sortAscending; } - /** Get a string that determines where a list of requests should begin. If the sort is - * ascending, the request list should begin at a string strictly after this string, and + /** Get a string that determines where a list of groups should begin. If the sort is + * ascending, the group list should begin at a string strictly after this string, and * vice versa for descending sorts. * @return the exclusion string. */ public Optional getExcludeUpTo() { return excludeUpTo; } + + /** Get the minimum role the user should have in the returned groups. + * @return the role. + */ + public Role getRole() { + return role; + } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((excludeUpTo == null) ? 0 : excludeUpTo.hashCode()); + result = prime * result + ((role == null) ? 0 : role.hashCode()); result = prime * result + (sortAscending ? 1231 : 1237); return result; } @@ -62,6 +78,13 @@ public boolean equals(Object obj) { } else if (!excludeUpTo.equals(other.excludeUpTo)) { return false; } + if (role == null) { + if (other.role != null) { + return false; + } + } else if (!role.equals(other.role)) { + return false; + } if (sortAscending != other.sortAscending) { return false; } @@ -83,6 +106,7 @@ public static class Builder { private boolean sortAscending = true; private Optional excludeUpTo = Optional.empty(); + private Role role = Role.NONE; private Builder() {} @@ -100,8 +124,8 @@ public Builder withNullableSortAscending(final Boolean sortAscending) { return this; } - /** Set a string that determines where a list of requests should begin. If the sort is - * ascending, the request list should begin at a string strictly after this string, + /** Set a string that determines where a list of groups should begin. If the sort is + * ascending, the group list should begin at a string strictly after this string, * and vice versa for descending sorts. * If null or whitespace only, no string is set. * The string is {@link String#trim()}ed. @@ -117,11 +141,20 @@ public Builder withNullableExcludeUpTo(final String excludeUpTo) { return this; } + /** Set the minimum role the user must have in the returned groups. + * @param role the role. + * @return this builder. + */ + public Builder withRole(final Role role) { + this.role = requireNonNull(role, "role"); + return this; + } + /** Build the {@link GetGroupsParams}. * @return the params. */ public GetGroupsParams build() { - return new GetGroupsParams(sortAscending, excludeUpTo); + return new GetGroupsParams(sortAscending, excludeUpTo, role); } } } diff --git a/src/us/kbase/groups/core/Group.java b/src/us/kbase/groups/core/Group.java index af2bb28d..10ffb8ef 100644 --- a/src/us/kbase/groups/core/Group.java +++ b/src/us/kbase/groups/core/Group.java @@ -131,6 +131,13 @@ public enum Role { /** The owner of the group. */ OWNER ("Owner"); + private static final Map REP_MAP = new HashMap<>(); + static { + for (final Role r: Role.values()) { + REP_MAP.put(r.getRepresentation(), r); + } + } + private final String representation; private Role(final String representation) { @@ -143,6 +150,18 @@ private Role(final String representation) { public String getRepresentation() { return representation; } + + /** Get a role based on a supplied representation. + * @param representation the representation of the role as a string. + * @return a role type. + * @throws IllegalArgumentException if there is no role matching the representation. + */ + public static Role fromRepresentation(final String representation) { + if (!REP_MAP.containsKey(representation)) { + throw new IllegalArgumentException("Invalid role: " + representation); + } + return REP_MAP.get(representation); + } } /** Get the role of a user within a group. diff --git a/src/us/kbase/groups/core/request/GroupRequest.java b/src/us/kbase/groups/core/request/GroupRequest.java index a1cd51e1..a4fbf1e6 100644 --- a/src/us/kbase/groups/core/request/GroupRequest.java +++ b/src/us/kbase/groups/core/request/GroupRequest.java @@ -342,7 +342,7 @@ public Builder withType(final RequestType type) { } /** Set the resource that is the target of this request. - * @param the type of the resource. + * @param type the type of the resource. * @param resource the resource. * @return this builder. */ diff --git a/src/us/kbase/test/groups/core/GetGroupsParamsTest.java b/src/us/kbase/test/groups/core/GetGroupsParamsTest.java index 9db4c1c6..ee0fe19b 100644 --- a/src/us/kbase/test/groups/core/GetGroupsParamsTest.java +++ b/src/us/kbase/test/groups/core/GetGroupsParamsTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import java.util.Optional; @@ -9,6 +10,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; import us.kbase.groups.core.GetGroupsParams; +import us.kbase.groups.core.Group.Role; +import us.kbase.test.groups.TestCommon; public class GetGroupsParamsTest { @@ -23,6 +26,7 @@ public void buildMinimal() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect role", p.getRole(), is(Role.NONE)); } @Test @@ -34,6 +38,7 @@ public void buildWithNulls() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect role", p.getRole(), is(Role.NONE)); } @Test @@ -41,10 +46,12 @@ public void buildWithDefaults() throws Exception { final GetGroupsParams p = GetGroupsParams.getBuilder() .withNullableExcludeUpTo(null) .withNullableSortAscending(true) + .withRole(Role.NONE) .build(); assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect role", p.getRole(), is(Role.NONE)); } @Test @@ -56,6 +63,7 @@ public void buildWithWhitespace() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); + assertThat("incorrect role", p.getRole(), is(Role.NONE)); } @Test @@ -63,10 +71,22 @@ public void buildMaximal() throws Exception { final GetGroupsParams p = GetGroupsParams.getBuilder() .withNullableExcludeUpTo(" foo ") .withNullableSortAscending(false) + .withRole(Role.ADMIN) .build(); assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.of("foo"))); assertThat("incorrect sort", p.isSortAscending(), is(false)); + assertThat("incorrect role", p.getRole(), is(Role.ADMIN)); + } + + @Test + public void withRoleFail() throws Exception { + try { + GetGroupsParams.getBuilder().withRole(null); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, new NullPointerException("role")); + } } } diff --git a/src/us/kbase/test/groups/core/GroupTest.java b/src/us/kbase/test/groups/core/GroupTest.java index 770309e6..89396182 100644 --- a/src/us/kbase/test/groups/core/GroupTest.java +++ b/src/us/kbase/test/groups/core/GroupTest.java @@ -396,6 +396,31 @@ public void roleValues() { Role.OWNER))); } + @Test + public void roleGetFromRepresentation() throws Exception { + assertThat("incorrect role", Role.fromRepresentation("None"), is(Role.NONE)); + assertThat("incorrect role", Role.fromRepresentation("Member"), is(Role.MEMBER)); + assertThat("incorrect role", Role.fromRepresentation("Admin"), is(Role.ADMIN)); + assertThat("incorrect role", Role.fromRepresentation("Owner"), is(Role.OWNER)); + } + + @Test + public void roleGetFromRepresentationFail() throws Exception { + roleGetFromRepresentationFail(null, new IllegalArgumentException("Invalid role: null")); + roleGetFromRepresentationFail(" \t ", new IllegalArgumentException( + "Invalid role: \t ")); + roleGetFromRepresentationFail("foo", new IllegalArgumentException("Invalid role: foo")); + } + + private void roleGetFromRepresentationFail(final String rep, final Exception expected) { + try { + Role.fromRepresentation(rep); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void failGetRole() throws Exception { final Group g = Group.getBuilder( From 158885e02c343d41aab4a9a11417c09b5071108e Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 16:02:03 -0700 Subject: [PATCH 23/39] Add include groups by role in mongo list groups method Requires new indexes --- RELEASE_NOTES.md | 9 ++ .../kbase/groups/storage/GroupsStorage.java | 2 + .../storage/mongo/MongoGroupsStorage.java | 31 +++++-- .../mongo/MongoGroupsStorageOpsTest.java | 91 +++++++++++++++++++ .../mongo/MongoGroupsStorageStartupTest.java | 8 +- 5 files changed, 130 insertions(+), 11 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 58008851..821b02b3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,9 +2,18 @@ ## 0.1.6 +### Admin notes: + +* The `group` collection `own_1` and `admin_1` MongoDB indexes have been replaced by + `own_1_id_1` and `admin_1_id_1` indexes and can be deleted. + +### Release notes + * Added the `/request/groups` endpoint. * Resource administrators can now see their resources in private groups for which they are not a member in the `/group/ endpoint`. +* Added the `ids` parameter to the `/groups` endpoint. +* Added `resourcetype` and `resource` parameters to the four request listing endpoints. ## 0.1.5 diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index b378f028..dd961c74 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -115,6 +115,8 @@ List getGroupNames(UserName user, Set groupID) /** Get groups in the system, sorted by the group ID. * At most 100 groups are returned. + * If the user is null and the user's role is not {@link Group.Role#NONE} no groups + * are returned. * @param params the parameters for getting the groups. * @param user an optional user. If no user is provided, only public groups are returned. * @return the groups. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 4cd3e94c..44ef3ae7 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -11,6 +11,7 @@ import java.time.Instant; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -56,6 +57,7 @@ import us.kbase.groups.core.GetGroupsParams; import us.kbase.groups.core.GetRequestsParams; import us.kbase.groups.core.UserName; +import us.kbase.groups.core.Group.Role; import us.kbase.groups.core.exceptions.GroupExistsException; import us.kbase.groups.core.exceptions.IllegalParameterException; import us.kbase.groups.core.exceptions.MissingParameterException; @@ -122,10 +124,10 @@ public class MongoGroupsStorage implements GroupsStorage { final Map, IndexOptions> groups = new HashMap<>(); // will probably need to sort by time at some point groups.put(Arrays.asList(Fields.GROUP_ID), IDX_UNIQ); - // find by owner - groups.put(Arrays.asList(Fields.GROUP_OWNER), null); - // find by admin - groups.put(Arrays.asList(Fields.GROUP_ADMINS), null); + // find by owner and sort by ID + groups.put(Arrays.asList(Fields.GROUP_OWNER, Fields.GROUP_ID), null); + // find by admin and sort by ID + groups.put(Arrays.asList(Fields.GROUP_ADMINS, Fields.GROUP_ID), null); // find public groups and sort by ID (not needed?) groups.put(Arrays.asList(Fields.GROUP_IS_PRIVATE, Fields.GROUP_ID), null); // find groups by member and sort by ID @@ -746,17 +748,32 @@ private List getList( public List getGroups(final GetGroupsParams params, final UserName user) throws GroupsStorageException { requireNonNull(params, "params"); + if (user == null && !params.getRole().equals(Role.NONE)) { // TODO NOW test in Groups and throw error + return Collections.emptyList(); + } final Document query = new Document(); if (params.getExcludeUpTo().isPresent()) { final String inequality = params.isSortAscending() ? "$gt" : "$lt"; query.append(Fields.GROUP_ID, new Document(inequality, params.getExcludeUpTo().get())); } + final String memberField = Fields.GROUP_MEMBERS + Fields.FIELD_SEP + + Fields.GROUP_MEMBER_NAME; if (user == null) { query.append(Fields.GROUP_IS_PRIVATE, false); - } else { + } else if (params.getRole().equals(Role.NONE)) { query.append("$or", Arrays.asList(new Document(Fields.GROUP_IS_PRIVATE, false), - new Document(Fields.GROUP_MEMBERS + Fields.FIELD_SEP + - Fields.GROUP_MEMBER_NAME, user.getName()))); + // members contains all members + new Document(memberField, user.getName()))); + } else if (params.getRole().equals(Role.OWNER)) { + query.append(Fields.GROUP_OWNER, user.getName()); + } else if (params.getRole().equals(Role.ADMIN)) { + query.append("$or", Arrays.asList( + new Document(Fields.GROUP_ADMINS, user.getName()), + new Document(Fields.GROUP_OWNER, user.getName()) + )); + } else { + // members contains all members + query.append(memberField, user.getName()); } // may want to allow alternate sorts later, will need indexes final Document sort = new Document(Fields.GROUP_ID, params.isSortAscending() ? 1 : -1); diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index aa47a53b..83ab3053 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -36,6 +36,7 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import us.kbase.groups.core.Group; +import us.kbase.groups.core.Group.Role; import us.kbase.groups.core.GroupID; import us.kbase.groups.core.GroupIDAndName; import us.kbase.groups.core.GroupIDNameMembership; @@ -1184,6 +1185,14 @@ private void failUpdateGroup( } } + @Test + public void getGroupsNoUserWithRole() throws Exception { + assertThat("incorrect get groups", + manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), null), + is(Collections.emptyList())); + } + @Test public void getGroupsEmpty() throws Exception { assertThat("incorrect get groups", @@ -1267,6 +1276,88 @@ public void getGroupsDefaultParams() throws Exception { ))); } + @Test + public void getGroupsWithUserRole() throws Exception { + final Group g1 = Group.getBuilder( + new GroupID("g1"), new GroupName("na"), toGUser("o"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withAdministrator(toGUser("a1")) + .withIsPrivate(true) + .build(); + final Group g2 = Group.getBuilder( + new GroupID("g2"), new GroupName("na"), toGUser("a1"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withAdministrator(toGUser("m1")) + .build(); + final Group g3 = Group.getBuilder( + new GroupID("g3"), new GroupName("na"), toGUser("o1"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withAdministrator(toGUser("a")) + .withMember(toGUser("m1")) + .build(); + final Group g4 = Group.getBuilder( + new GroupID("g4"), new GroupName("na"), toGUser("o1"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withMember(toGUser("a1")) + .withMember(toGUser("m")) + .withMember(toGUser("o")) + .build(); + + manager.storage.createGroup(g1); + manager.storage.createGroup(g2); + manager.storage.createGroup(g3); + manager.storage.createGroup(g4); + + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().build(), null), + is(Arrays.asList(g2, g3, g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.NONE).build(), null), + is(Arrays.asList(g2, g3, g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.NONE).build(), new UserName("a1")), + is(Arrays.asList(g1, g2, g3, g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.NONE).build(), new UserName("m")), + is(Arrays.asList(g2, g3, g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("a1")), + is(Arrays.asList(g1, g2, g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("m")), + is(Arrays.asList(g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("o")), + is(Arrays.asList(g1, g4))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("v")), + is(Collections.emptyList())); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("a1")), + is(Arrays.asList(g1, g2))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("m1")), + is(Arrays.asList(g2))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("m")), + is(Collections.emptyList())); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("o")), + is(Arrays.asList(g1))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("m1")), + is(Collections.emptyList())); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("a1")), + is(Arrays.asList(g2))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("o")), + is(Arrays.asList(g1))); + assertThat("incorrect groups", manager.storage.getGroups( + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("o1")), + is(Arrays.asList(g3, g4))); + } + @Test public void getGroupsPublicAndPrivate() throws Exception { final Group g1 = Group.getBuilder( diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java index 61723159..afd017e1 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java @@ -211,12 +211,12 @@ public void indexesGroups() { .append("name", "id_1") .append("ns", col), new Document("v", manager.indexVer) - .append("key", new Document("own", 1)) - .append("name", "own_1") + .append("key", new Document("own", 1).append("id", 1)) + .append("name", "own_1_id_1") .append("ns", col), new Document("v", manager.indexVer) - .append("key", new Document("admin", 1)) - .append("name", "admin_1") + .append("key", new Document("admin", 1).append("id", 1)) + .append("name", "admin_1_id_1") .append("ns", col), new Document("v", manager.indexVer) .append("key", new Document("_id", 1)) From 49657816ca721e421eb3daeaa5cf553b2fab2fda Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 16:19:05 -0700 Subject: [PATCH 24/39] Throw an error if a anon user attempts to filter groups by role. --- src/us/kbase/groups/core/Groups.java | 8 +++- .../kbase/groups/service/api/GroupsAPI.java | 3 +- src/us/kbase/test/groups/core/GroupsTest.java | 37 +++++++++++++++++-- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index e6980e8c..d510105e 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -21,6 +21,7 @@ import java.util.Map.Entry; import java.util.stream.Collectors; +import us.kbase.groups.core.Group.Role; import us.kbase.groups.core.exceptions.AuthenticationException; import us.kbase.groups.core.exceptions.ClosedRequestException; import us.kbase.groups.core.exceptions.GroupExistsException; @@ -540,10 +541,15 @@ public Map groupsHaveRequests( * @throws GroupsStorageException if an error occurs contacting the storage system. * @throws InvalidTokenException if the token is invalid. * @throws AuthenticationException if authentication fails. + * @throws UnauthorizedException if a role is specified but no token is provided. */ public List getGroups(final Token userToken, final GetGroupsParams params) - throws GroupsStorageException, InvalidTokenException, AuthenticationException { + throws GroupsStorageException, InvalidTokenException, AuthenticationException, + UnauthorizedException { checkNotNull(params, "params"); + if (userToken == null && !params.getRole().equals(Role.NONE)) { + throw new UnauthorizedException("A token is required when filtering groups by role"); + } final UserName user = getOptionalUser(userToken); return storage.getGroups(params, user).stream() .map(g -> toMinimalView(user, g)).collect(Collectors.toList()); diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index f3632ae2..ce14a769 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -86,7 +86,8 @@ public List> getGroups( @QueryParam(Fields.GET_GROUPS_SORT_ORDER) final String order, @QueryParam(Fields.GET_GROUPS_IDS) final String groupIDs) throws GroupsStorageException, IllegalParameterException, NoTokenProvidedException, - InvalidTokenException, AuthenticationException, NoSuchGroupException { + InvalidTokenException, AuthenticationException, NoSuchGroupException, + UnauthorizedException { final List gids = getGroupIDs(groupIDs); final Token t = getToken(token, false); final List grps; diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 628da9fa..d81fc6be 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -55,6 +55,7 @@ import us.kbase.groups.core.UUIDGenerator; import us.kbase.groups.core.UserHandler; import us.kbase.groups.core.UserName; +import us.kbase.groups.core.Group.Role; import us.kbase.groups.core.exceptions.AuthenticationException; import us.kbase.groups.core.exceptions.ClosedRequestException; import us.kbase.groups.core.exceptions.ErrorType; @@ -1785,12 +1786,42 @@ public void getGroupsWithPrivateGroup() throws Exception { } @Test - public void getGroupsFail() throws Exception { + public void getGroupsWithRole() throws Exception { + // just tests that getting groups with a token & a role doesn't throw an error. + final TestMocks mocks = initTestMocks(); + + final Group g1 = Group.getBuilder(new GroupID("g1"), new GroupName("n1"), + GroupUser.getBuilder(new UserName("o1"), inst(10000)).build(), + new CreateAndModTimes(inst(1000))) + .build(); + + when(mocks.userHandler.getUser(new Token("t1"))).thenReturn(new UserName("m1")); + when(mocks.storage.getGroups(any(), eq(new UserName("m1")))) + .thenReturn(Arrays.asList(g1)); + + for (final Role r: Role.values()) { + assertThat("incorrect groups", mocks.groups.getGroups( + new Token("t1"), GetGroupsParams.getBuilder().withRole(r).build()), + is(Arrays.asList(GroupView.getBuilder(g1, new UserName("m1")).build()))); + } + } + + @Test + public void getGroupsFailBadArgs() throws Exception { + getGroupsFail(new Token("t"), null, new NullPointerException("params")); + for (final Role r: set(Role.MEMBER, Role.ADMIN, Role.OWNER)) { + getGroupsFail(null, GetGroupsParams.getBuilder().withRole(r).build(), + new UnauthorizedException( + "A token is required when filtering groups by role")); + } + } + + private void getGroupsFail(final Token t, final GetGroupsParams p, final Exception expected) { try { - initTestMocks().groups.getGroups(null, (GetGroupsParams) null); + initTestMocks().groups.getGroups(t, p); fail("expected exception"); } catch (Exception got) { - TestCommon.assertExceptionCorrect(got, new NullPointerException("params")); + TestCommon.assertExceptionCorrect(got, expected); } } From 93c158b40c778bf85cb1a7dc961bef4697fcebe8 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 17:16:31 -0700 Subject: [PATCH 25/39] Add list groups role filter to API --- README.md | 4 +- RELEASE_NOTES.md | 2 +- src/us/kbase/groups/core/Groups.java | 4 +- .../kbase/groups/service/api/APICommon.java | 23 +++++-- src/us/kbase/groups/service/api/Fields.java | 2 + .../kbase/groups/service/api/GroupsAPI.java | 25 +------- .../kbase/groups/storage/GroupsStorage.java | 2 +- .../storage/mongo/MongoGroupsStorage.java | 2 +- .../groups/service/api/APICommonTest.java | 62 ++++++++++++++----- .../groups/service/api/GroupsAPITest.java | 32 ++++++---- 10 files changed, 95 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index f6ee01b9..59ae596f 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ RETURNS: ``` AUTHORIZATION OPTIONAL -GET /group[?excludeupto=&order=&groupids=] +GET /group[?excludeupto=&order=&role=&groupids=] RETURNS: A list of Groups. Only the id, private, name, owner, role, memcount, rescount, custom, @@ -264,6 +264,8 @@ The query parameters are all optional: depending on the sort order. `asc` and `desc` sorts will include groups with group IDs, respectively, after and before the `excludeupto` string, non-inclusive. This can be used to page through the groups if needed. +* `role` - Filters the group list by a minimum user role, one of `Member`, `Admin`, + or `Owner`. If a role is supplied an authorization token must also be supplied. * `groupids` - list specific groups by ID in a comma separated list (e.g. `?groupids=groupid1,groupid2,...,groupidN`). If this parameter is specified, all other parameters are ignored. This method of listing groups is much faster than getting diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 821b02b3..617ba5f7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,7 +12,7 @@ * Added the `/request/groups` endpoint. * Resource administrators can now see their resources in private groups for which they are not a member in the `/group/ endpoint`. -* Added the `ids` parameter to the `/groups` endpoint. +* Added the `ids` and `role` parameters to the `/groups` endpoint. * Added `resourcetype` and `resource` parameters to the four request listing endpoints. ## 0.1.5 diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index d510105e..fac257c1 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -455,7 +455,7 @@ public boolean getGroupExists(final GroupID groupID) throws GroupsStorageExcepti */ public List getGroupNames( final Token userToken, - final Set groupIDs) + final Collection groupIDs) throws InvalidTokenException, AuthenticationException, NoSuchGroupException, GroupsStorageException, IllegalParameterException { checkNoNullsInCollection(groupIDs, "groupIDs"); @@ -496,7 +496,7 @@ public List getMemberGroups(final Token userToken) */ public Map groupsHaveRequests( final Token userToken, - final Set groupIDs) + final Collection groupIDs) throws InvalidTokenException, AuthenticationException, UnauthorizedException, NoSuchGroupException, GroupsStorageException, IllegalParameterException { checkNoNullsInCollection(groupIDs, "groupIDs"); diff --git a/src/us/kbase/groups/service/api/APICommon.java b/src/us/kbase/groups/service/api/APICommon.java index 779f5a67..a8b3e905 100644 --- a/src/us/kbase/groups/service/api/APICommon.java +++ b/src/us/kbase/groups/service/api/APICommon.java @@ -267,6 +267,7 @@ public static Instant epochMilliStringToInstant(final String epochMilli) * Null or whitespace only values are ignored. * @param sortDirection the direction of the sort - 'asc' for an ascending sort, and 'desc' * for a descending sort. + * @param role the minimum role the user must possess. * @param defaultSort if sortDirection is null or whitespace only, this value is used instead. * true sets an ascending sort, false sets a descending sort. * @return the get groups parameters. @@ -275,10 +276,18 @@ public static Instant epochMilliStringToInstant(final String epochMilli) public static GetGroupsParams getGroupsParams( final String excludeUpTo, final String sortDirection, + final String role, final boolean defaultSort) throws IllegalParameterException { final GetGroupsParams.Builder b = GetGroupsParams.getBuilder() .withNullableExcludeUpTo(excludeUpTo); + if (!isNullOrEmpty(role)) { + try { + b.withRole(Role.fromRepresentation(role)); + } catch (IllegalArgumentException e) { + throw new IllegalParameterException(e.getMessage(), e); + } + } setSortDirection(sortDirection, defaultSort, s -> b.withNullableSortAscending(s)); return b.build(); } @@ -299,21 +308,23 @@ private static void setSortDirection( } } - /** Split a comma separated string into a set of group IDs. Whitespace only entries are + /** Split a comma separated string into a list of group IDs. Whitespace only entries are * ignored. * @param commaSeparatedGroupIDs the group IDs as a comma separated string. * @return the group IDs. * @throws IllegalParameterException if an ID is illegal. */ - public static Set toGroupIDs(final String commaSeparatedGroupIDs) + public static List toGroupIDs(final String commaSeparatedGroupIDs) throws IllegalParameterException { - final Set groupIDs = new HashSet<>(); - for (final String id: requireNonNull(commaSeparatedGroupIDs, "commaSeparatedGroupIDs") - .split(",")) { + final List groupIDs = new LinkedList<>(); + if (commaSeparatedGroupIDs == null) { + return groupIDs; + } + for (final String id: commaSeparatedGroupIDs.split(",")) { // can't use streams due to checked exceptions if (!id.trim().isEmpty()) { try { - groupIDs.add(new GroupID(id)); + groupIDs.add(new GroupID(id.trim())); } catch (MissingParameterException e) { throw new RuntimeException("This is impossible. It didn't happen.", e); } diff --git a/src/us/kbase/groups/service/api/Fields.java b/src/us/kbase/groups/service/api/Fields.java index b5716561..53d7d96d 100644 --- a/src/us/kbase/groups/service/api/Fields.java +++ b/src/us/kbase/groups/service/api/Fields.java @@ -109,6 +109,8 @@ public class Fields { public static final String GET_GROUPS_SORT_ORDER = "order"; /** Determine which groups to list. */ public static final String GET_GROUPS_IDS = "groupids"; + /** Determine the user role to require. */ + public static final String GET_GROUPS_ROLE = "role"; /* *********************** * request listing fields diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index ce14a769..07f522e3 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -9,7 +9,6 @@ import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -84,40 +83,22 @@ public List> getGroups( @HeaderParam(HEADER_TOKEN) final String token, @QueryParam(Fields.GET_GROUPS_EXCLUDE_UP_TO) final String excludeUpTo, @QueryParam(Fields.GET_GROUPS_SORT_ORDER) final String order, + @QueryParam(Fields.GET_GROUPS_ROLE) final String role, @QueryParam(Fields.GET_GROUPS_IDS) final String groupIDs) throws GroupsStorageException, IllegalParameterException, NoTokenProvidedException, InvalidTokenException, AuthenticationException, NoSuchGroupException, UnauthorizedException { - final List gids = getGroupIDs(groupIDs); + final List gids = APICommon.toGroupIDs(groupIDs); final Token t = getToken(token, false); final List grps; if (!gids.isEmpty()) { grps = groups.getGroups(t, gids); } else { - grps = groups.getGroups(t, getGroupsParams(excludeUpTo, order, true)); + grps = groups.getGroups(t, getGroupsParams(excludeUpTo, order, role, true)); } return grps.stream().map(g -> toGroupJSON(g)).collect(Collectors.toList()); } - private List getGroupIDs(final String groupIDsCommaSepList) - throws IllegalParameterException { - final List ret = new LinkedList<>(); - if (groupIDsCommaSepList == null) { - return ret; - } - final String[] gids = groupIDsCommaSepList.split(","); - for (final String g: gids) { - if (!g.trim().isEmpty()) { - try { - ret.add(new GroupID(g.trim())); - } catch (MissingParameterException e) { - throw new RuntimeException("This should be impossible", e); - } - } - } - return ret; - } - private static Map getCustomFieldsAndTypeCheck( final Object customFields, final String fieldName) diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index dd961c74..228de792 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -89,7 +89,7 @@ Set getGroups(Collection groupIDs) * @throws GroupsStorageException if an error occurs contacting the storage system. * @throws NoSuchGroupException if there is no group with one of the given IDs. */ - List getGroupNames(UserName user, Set groupID) + List getGroupNames(UserName user, Collection groupID) throws GroupsStorageException, NoSuchGroupException; /** Check whether a group exists. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 44ef3ae7..0187de4f 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -615,7 +615,7 @@ public Set getGroups(final Collection groupIDs) @Override public List getGroupNames( final UserName user, - final Set groupIDs) + final Collection groupIDs) throws NoSuchGroupException, GroupsStorageException { checkNoNullsInCollection(groupIDs, "groupIDs"); final Document projection = new Document(Fields.GROUP_ID, 1) diff --git a/src/us/kbase/test/groups/service/api/APICommonTest.java b/src/us/kbase/test/groups/service/api/APICommonTest.java index 7b022261..6adcef6a 100644 --- a/src/us/kbase/test/groups/service/api/APICommonTest.java +++ b/src/us/kbase/test/groups/service/api/APICommonTest.java @@ -5,7 +5,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static us.kbase.test.groups.TestCommon.inst; -import static us.kbase.test.groups.TestCommon.set; import java.time.Instant; import java.util.Arrays; @@ -29,6 +28,7 @@ import us.kbase.groups.core.Token; import us.kbase.groups.core.UserName; import us.kbase.groups.core.Group.Builder; +import us.kbase.groups.core.Group.Role; import us.kbase.groups.core.exceptions.ErrorType; import us.kbase.groups.core.exceptions.IllegalParameterException; import us.kbase.groups.core.exceptions.MissingParameterException; @@ -691,11 +691,11 @@ private void failEpochMilliStringToInstant(final String em, final Exception expe @Test public void getGroupParamsNulls() throws Exception { - final GetGroupsParams p = APICommon.getGroupsParams(null, null, true); + final GetGroupsParams p = APICommon.getGroupsParams(null, null, null, true); assertThat("incorrect params", p, is(GetGroupsParams.getBuilder().build())); - final GetGroupsParams p2 = APICommon.getGroupsParams(null, null, false); + final GetGroupsParams p2 = APICommon.getGroupsParams(null, null, null, false); assertThat("incorrect params", p2, is(GetGroupsParams.getBuilder() .withNullableSortAscending(false).build())); @@ -704,11 +704,11 @@ public void getGroupParamsNulls() throws Exception { @Test public void getGroupParamsWhitespace() throws Exception { final String ws = " \t "; - final GetGroupsParams p = APICommon.getGroupsParams(ws, ws, true); + final GetGroupsParams p = APICommon.getGroupsParams(ws, ws, ws, true); assertThat("incorrect params", p, is(GetGroupsParams.getBuilder().build())); - final GetGroupsParams p2 = APICommon.getGroupsParams(ws, ws, false); + final GetGroupsParams p2 = APICommon.getGroupsParams(ws, ws, ws, false); assertThat("incorrect params", p2, is(GetGroupsParams.getBuilder() .withNullableSortAscending(false).build())); @@ -716,41 +716,69 @@ public void getGroupParamsWhitespace() throws Exception { @Test public void getGroupParamsValues() throws Exception { - final GetGroupsParams p = APICommon.getGroupsParams(" foo ", "asc", false); + final GetGroupsParams p = APICommon.getGroupsParams(" foo ", "asc", "Member", false); assertThat("incorrect params", p, is(GetGroupsParams.getBuilder() - .withNullableExcludeUpTo("foo").build())); + .withRole(Role.MEMBER) + .withNullableExcludeUpTo("foo") + .build())); - final GetGroupsParams p2 = APICommon.getGroupsParams(" \t bar ", "desc", true); + final GetGroupsParams p2 = APICommon.getGroupsParams(" \t bar ", "desc", "Admin", true); assertThat("incorrect params", p2, is(GetGroupsParams.getBuilder() + .withRole(Role.ADMIN) .withNullableExcludeUpTo("bar") - .withNullableSortAscending(false).build())); + .withNullableSortAscending(false) + .build())); + + final GetGroupsParams p3 = APICommon.getGroupsParams(" foo ", "asc", "Owner", false); + + assertThat("incorrect params", p3, is(GetGroupsParams.getBuilder() + .withRole(Role.OWNER) + .withNullableExcludeUpTo("foo") + .build())); + + final GetGroupsParams p4 = APICommon.getGroupsParams(" foo ", "asc", "None", false); + + assertThat("incorrect params", p4, is(GetGroupsParams.getBuilder() + .withRole(Role.NONE) + .withNullableExcludeUpTo("foo") + .build())); } @Test - public void getGroupParamsFail() throws Exception { + public void getGroupParamsFailBadArgs() throws Exception { + getGroupParamsFail("asd", null, new IllegalParameterException( + "Invalid sort direction: asd")); + getGroupParamsFail(null, "member", new IllegalParameterException("Invalid role: member")); + } + + private void getGroupParamsFail( + final String sort, + final String role, + final Exception expected) { try { - APICommon.getGroupsParams(null, "asd", false); + APICommon.getGroupsParams(null, sort, role, false); fail("expected exception"); } catch (Exception got) { - TestCommon.assertExceptionCorrect(got, new IllegalParameterException( - "Invalid sort direction: asd")); + TestCommon.assertExceptionCorrect(got, expected); } } @Test public void toGroupIDs() throws Exception { - assertThat("incorrect group IDs", APICommon.toGroupIDs(" \t "), is(set())); + final List mt = Collections.emptyList(); + assertThat("incorrect group IDs", APICommon.toGroupIDs(null), is(mt)); + assertThat("incorrect group IDs", APICommon.toGroupIDs(" \t "), is(mt)); + assertThat("incorrect group IDs", APICommon.toGroupIDs(" \t , , "), is(mt)); assertThat("incorrect group IDs", APICommon.toGroupIDs(" foo, \t , bar ,baz, \t"), - is(set(new GroupID("foo"), new GroupID("bar"), new GroupID("baz")))); + is(Arrays.asList(new GroupID("foo"), new GroupID("bar"), new GroupID("baz")))); } @Test public void failToGroupIDs() throws Exception { - failToGroupIDs(null, new NullPointerException("commaSeparatedGroupIDs")); failToGroupIDs(" \t , foo, bad*name, bar", new IllegalParameterException( - ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id bad*name: *")); + ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id bad*name: *")); } private void failToGroupIDs(final String ids, final Exception expected) { diff --git a/src/us/kbase/test/groups/service/api/GroupsAPITest.java b/src/us/kbase/test/groups/service/api/GroupsAPITest.java index e0985e72..70162715 100644 --- a/src/us/kbase/test/groups/service/api/GroupsAPITest.java +++ b/src/us/kbase/test/groups/service/api/GroupsAPITest.java @@ -29,6 +29,7 @@ import us.kbase.groups.core.GetRequestsParams; import us.kbase.groups.core.Group; import us.kbase.groups.core.Group.Builder; +import us.kbase.groups.core.Group.Role; import us.kbase.groups.core.GroupCreationParams; import us.kbase.groups.core.GroupID; import us.kbase.groups.core.GroupName; @@ -267,27 +268,30 @@ private static Builder getGroupMaxBuilder() @Test public void getGroupsNulls() throws Exception { - getGroups(null, null, null, null, null, GetGroupsParams.getBuilder().build()); + getGroups(null, null, null, null, null, null, GetGroupsParams.getBuilder().build()); } @Test public void getGroupsWhitespace() throws Exception { - getGroups(" \t ", " \t ", " \t ", " \t ", null, + getGroups(" \t ", " \t ", " \t ", " \t ", " \t ", null, GetGroupsParams.getBuilder().build()); } @Test public void getGroupsWhitespaceValuesAsc() throws Exception { - getGroups(" tok \t ", " foo \t ", " asc \t ", " , \t , ", + getGroups(" tok \t ", " foo \t ", " asc \t ", "Member", " , \t , ", new Token(" tok \t "), GetGroupsParams.getBuilder() - .withNullableExcludeUpTo("foo").build()); + .withRole(Role.MEMBER) + .withNullableExcludeUpTo("foo") + .build()); } @Test public void getGroupsWhitespaceValuesDesc() throws Exception { - getGroups("t", " foo \t ", " desc \t ", ",", new Token("t"), + getGroups("t", " foo \t ", " desc \t ", "Admin", ",", new Token("t"), GetGroupsParams.getBuilder() + .withRole(Role.ADMIN) .withNullableExcludeUpTo("foo") .withNullableSortAscending(false).build()); } @@ -296,6 +300,7 @@ private void getGroups( final String token, final String excludeUpTo, final String order, + final String role, final String ids, // this must be null or contain ws (with commas) final Token expectedToken, final GetGroupsParams expected) @@ -309,7 +314,7 @@ private void getGroups( .withPublicUserFieldDeterminer(f -> f.getField().equals("something")) .build())); final List> ret = new GroupsAPI(g) - .getGroups(token, excludeUpTo, order, ids); + .getGroups(token, excludeUpTo, order, role, ids); assertThat("incorrect groups", ret, is(Arrays.asList(GROUP_MAX_JSON_MIN, GROUP_MIN_JSON_MIN))); @@ -348,7 +353,7 @@ private void getGroupsWithIDs( .build())); final List> ret = new GroupsAPI(g) - .getGroups(token, "id", "asc", "id2 , priv, id "); + .getGroups(token, "id", "asc", "Owner", "id2 , priv, id "); assertThat("incorrect groups", ret, is(Arrays.asList( @@ -361,9 +366,11 @@ private void getGroupsWithIDs( public void getGroupsFailBadArgs() throws Exception { final Groups g = mock(Groups.class); - failGetGroups(g, "t", null, " asd ", null, new IllegalParameterException( + failGetGroups(g, "t", null, " asd ", null, null, new IllegalParameterException( "Invalid sort direction: asd")); - failGetGroups(g, "t", null, null, " id1 , id*bad", new IllegalParameterException( + failGetGroups(g, "t", null, null, "owner", null, new IllegalParameterException( + "Invalid role: owner")); + failGetGroups(g, "t", null, null, null, " id1 , id*bad", new IllegalParameterException( ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id id*bad: *")); } @@ -372,10 +379,11 @@ private void failGetGroups( final String token, final String excludeUpTo, final String order, + final String role, final String ids, final Exception expected) { try { - new GroupsAPI(g).getGroups(token, excludeUpTo, order, ids); + new GroupsAPI(g).getGroups(token, excludeUpTo, order, role, ids); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); @@ -922,8 +930,8 @@ public void getGroupWithResources() throws Exception { when(g.getGroups(new Token("toke2"), GetGroupsParams.getBuilder().build())) .thenReturn(Arrays.asList(gv.withStandardView(false).build())); - final Map retmin = new GroupsAPI(g).getGroups("toke2", null, null, null) - .get(0); + final Map retmin = new GroupsAPI(g) + .getGroups("toke2", null, null, null, null).get(0); final Map expectedmin = new HashMap<>(); expectedmin.putAll(GROUP_MAX_JSON_MIN); expectedmin.put("role", "Admin"); From e306b221e335329647dc4ae65a3608c122793e16 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 17:39:59 -0700 Subject: [PATCH 26/39] Fix tests --- src/us/kbase/test/groups/service/api/NamesAPITest.java | 8 ++++---- src/us/kbase/test/groups/service/api/RequestAPITest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/us/kbase/test/groups/service/api/NamesAPITest.java b/src/us/kbase/test/groups/service/api/NamesAPITest.java index 39c0e4a6..9a278f5d 100644 --- a/src/us/kbase/test/groups/service/api/NamesAPITest.java +++ b/src/us/kbase/test/groups/service/api/NamesAPITest.java @@ -5,7 +5,6 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static us.kbase.test.groups.TestCommon.set; import java.util.Arrays; import java.util.List; @@ -32,7 +31,8 @@ public void getGroupNamesAnonymous() throws Exception { final NamesAPI napi = new NamesAPI(g); - when(g.getGroupNames(null, set(new GroupID("i3"), new GroupID("i1"), new GroupID("i22")))) + when(g.getGroupNames(null, Arrays.asList( + new GroupID("i3"), new GroupID("i1"), new GroupID("i22")))) .thenReturn(Arrays.asList( GroupIDNameMembership.getBuilder(new GroupID("i1")) .withGroupName(new GroupName("n1")) @@ -72,7 +72,7 @@ public void getGroupNames() throws Exception { final NamesAPI napi = new NamesAPI(g); when(g.getGroupNames(new Token("token"), - set(new GroupID("i3"), new GroupID("i1"), new GroupID("i22")))) + Arrays.asList(new GroupID("i3"), new GroupID("i1"), new GroupID("i22")))) .thenReturn(Arrays.asList( GroupIDNameMembership.getBuilder(new GroupID("i1")) .withGroupName(new GroupName("n1")) @@ -106,7 +106,7 @@ public void getGroupNames() throws Exception { @Test public void failGetGroupNames() throws Exception { failGetGroupNames(" id1, bad*id, ", new IllegalParameterException( - ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id bad*id: *")); + ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id bad*id: *")); } private void failGetGroupNames(final String ids, final Exception expected) { diff --git a/src/us/kbase/test/groups/service/api/RequestAPITest.java b/src/us/kbase/test/groups/service/api/RequestAPITest.java index 7c379bed..429f91c7 100644 --- a/src/us/kbase/test/groups/service/api/RequestAPITest.java +++ b/src/us/kbase/test/groups/service/api/RequestAPITest.java @@ -1009,7 +1009,7 @@ public void groupsHaveRequests() throws Exception { when(g.groupsHaveRequests( new Token("tokyn"), - set(new GroupID("id1"), new GroupID("id2"), new GroupID("id3")))) + Arrays.asList(new GroupID("id1"), new GroupID("id2"), new GroupID("id3")))) .thenReturn(ImmutableMap.of( new GroupID("id1"), GroupHasRequests.NONE, new GroupID("id2"), GroupHasRequests.OLD, From 09a8e669c4b9264f5f026b9caf5d38f16964fb69 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 17:53:13 -0700 Subject: [PATCH 27/39] Add resource ID to get groups parameters. --- README.md | 1 - src/us/kbase/groups/core/GetGroupsParams.java | 64 +++++++++++++++++-- .../test/groups/core/GetGroupsParamsTest.java | 30 +++++++++ 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 59ae596f..665c0794 100644 --- a/README.md +++ b/README.md @@ -1066,7 +1066,6 @@ see /design/*.md * Every filter & sort combination (usually) requires a new MongoDB index & more time & maintenance cost, so choose carefully * Remember - skip is evil - * Find groups where I'm (owner / admin / member) * Find groups where user X is an owner or admin * Find groups where user X is a member and I'm a member * Find groups that contain workspaces I administrate diff --git a/src/us/kbase/groups/core/GetGroupsParams.java b/src/us/kbase/groups/core/GetGroupsParams.java index c8006e2b..5e8f2e02 100644 --- a/src/us/kbase/groups/core/GetGroupsParams.java +++ b/src/us/kbase/groups/core/GetGroupsParams.java @@ -6,6 +6,8 @@ import java.util.Optional; import us.kbase.groups.core.Group.Role; +import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceType; /** Parameters for getting a list of groups. * @author gaprice@lbl.gov @@ -13,17 +15,25 @@ */ public class GetGroupsParams { + // pretty similar to GetRequestsParams, but inheritance + builders is a pain. + private final boolean sortAscending; private final Optional excludeUpTo; private final Role role; + private final Optional resourceType; + private final Optional resourceID; private GetGroupsParams( final boolean sortAscending, final Optional excludeUpTo, - final Role role) { + final Role role, + final Optional resourceType, + final Optional resourceID) { this.sortAscending = sortAscending; this.excludeUpTo = excludeUpTo; this.role = role; + this.resourceType = resourceType; + this.resourceID = resourceID; } /** Get whether the list should be sorted in ascending or descending order. @@ -48,12 +58,32 @@ public Optional getExcludeUpTo() { public Role getRole() { return role; } + + /** Get the resource type that must limit the list of groups. If the type is present, + * {@link #getResourceID()} will always return a resource ID. The combination of the two + * must limit the list of groups. + * @return the resource type that all the groups must possess. + */ + public Optional getResourceType() { + return resourceType; + } + + /** Get the resource ID that must limit the list of groups. If the ID is present, + * {@link #getResourceType()} will always return a resource type. The combination of the two + * must limit the list of groups. + * @return the resource ID that all the groups must possess. + */ + public Optional getResourceID() { + return resourceID; + } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((excludeUpTo == null) ? 0 : excludeUpTo.hashCode()); + result = prime * result + ((resourceID == null) ? 0 : resourceID.hashCode()); + result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode()); result = prime * result + ((role == null) ? 0 : role.hashCode()); result = prime * result + (sortAscending ? 1231 : 1237); return result; @@ -78,11 +108,21 @@ public boolean equals(Object obj) { } else if (!excludeUpTo.equals(other.excludeUpTo)) { return false; } - if (role == null) { - if (other.role != null) { + if (resourceID == null) { + if (other.resourceID != null) { + return false; + } + } else if (!resourceID.equals(other.resourceID)) { + return false; + } + if (resourceType == null) { + if (other.resourceType != null) { return false; } - } else if (!role.equals(other.role)) { + } else if (!resourceType.equals(other.resourceType)) { + return false; + } + if (role != other.role) { return false; } if (sortAscending != other.sortAscending) { @@ -107,6 +147,8 @@ public static class Builder { private boolean sortAscending = true; private Optional excludeUpTo = Optional.empty(); private Role role = Role.NONE; + private Optional resourceType = Optional.empty(); + private Optional resourceID = Optional.empty(); private Builder() {} @@ -150,11 +192,23 @@ public Builder withRole(final Role role) { return this; } + /** Set a resource ID that limits the list of groups to include only groups + * that contain that resource ID. + * @param type the type of the resource. + * @param id the resource ID. + * @return this builder. + */ + public Builder withResource(final ResourceType type, final ResourceID id) { + this.resourceType = Optional.of(requireNonNull(type, "type")); + this.resourceID = Optional.of(requireNonNull(id, "id")); + return this; + } + /** Build the {@link GetGroupsParams}. * @return the params. */ public GetGroupsParams build() { - return new GetGroupsParams(sortAscending, excludeUpTo, role); + return new GetGroupsParams(sortAscending, excludeUpTo, role, resourceType, resourceID); } } } diff --git a/src/us/kbase/test/groups/core/GetGroupsParamsTest.java b/src/us/kbase/test/groups/core/GetGroupsParamsTest.java index ee0fe19b..74f6fa0a 100644 --- a/src/us/kbase/test/groups/core/GetGroupsParamsTest.java +++ b/src/us/kbase/test/groups/core/GetGroupsParamsTest.java @@ -11,6 +11,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; import us.kbase.groups.core.GetGroupsParams; import us.kbase.groups.core.Group.Role; +import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceType; import us.kbase.test.groups.TestCommon; public class GetGroupsParamsTest { @@ -27,6 +29,8 @@ public void buildMinimal() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); assertThat("incorrect role", p.getRole(), is(Role.NONE)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -39,6 +43,8 @@ public void buildWithNulls() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); assertThat("incorrect role", p.getRole(), is(Role.NONE)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -52,6 +58,8 @@ public void buildWithDefaults() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); assertThat("incorrect role", p.getRole(), is(Role.NONE)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -64,6 +72,8 @@ public void buildWithWhitespace() throws Exception { assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.empty())); assertThat("incorrect sort", p.isSortAscending(), is(true)); assertThat("incorrect role", p.getRole(), is(Role.NONE)); + assertThat("incorrect type", p.getResourceType(), is(Optional.empty())); + assertThat("incorrect type", p.getResourceID(), is(Optional.empty())); } @Test @@ -72,11 +82,14 @@ public void buildMaximal() throws Exception { .withNullableExcludeUpTo(" foo ") .withNullableSortAscending(false) .withRole(Role.ADMIN) + .withResource(new ResourceType("t"), new ResourceID("id")) .build(); assertThat("incorrect exclude", p.getExcludeUpTo(), is(Optional.of("foo"))); assertThat("incorrect sort", p.isSortAscending(), is(false)); assertThat("incorrect role", p.getRole(), is(Role.ADMIN)); + assertThat("incorrect type", p.getResourceType(), is(Optional.of(new ResourceType("t")))); + assertThat("incorrect type", p.getResourceID(), is(Optional.of(new ResourceID("id")))); } @Test @@ -89,4 +102,21 @@ public void withRoleFail() throws Exception { } } + @Test + public void withResourceFailNulls() throws Exception { + withResourceFail(null, new ResourceID("i"), new NullPointerException("type")); + withResourceFail(new ResourceType("t"), null, new NullPointerException("id")); + } + + private void withResourceFail( + final ResourceType t, + final ResourceID i, + final Exception expected) { + try { + GetGroupsParams.getBuilder().withResource(t, i); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } } From c0e7bc906f6c018479c62f7baca0b97d01599205 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Thu, 14 Mar 2019 19:15:14 -0700 Subject: [PATCH 28/39] Pass resource types to MongoStorage & create indexes Will need resource indexes for filtering groups by resource ID --- src/us/kbase/groups/build/GroupsBuilder.java | 4 +- .../storage/mongo/MongoGroupsStorage.java | 52 +++++++++-- .../test/groups/MongoStorageTestManager.java | 6 +- .../mongo/MongoGroupsStorageStartupTest.java | 92 +++++++++++++++---- 4 files changed, 126 insertions(+), 28 deletions(-) diff --git a/src/us/kbase/groups/build/GroupsBuilder.java b/src/us/kbase/groups/build/GroupsBuilder.java index 666ca469..433b0faf 100644 --- a/src/us/kbase/groups/build/GroupsBuilder.java +++ b/src/us/kbase/groups/build/GroupsBuilder.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.net.URISyntaxException; +import java.util.Arrays; import org.slf4j.LoggerFactory; @@ -231,7 +232,8 @@ private GroupsStorage buildStorage( e.getMessage(), e); } //TODO TEST authenticate to db, write actual test with authentication - return new MongoGroupsStorage(db); + return new MongoGroupsStorage( + db, Arrays.asList(RESOURCE_TYPE_WORKSPACE, RESOURCE_TYPE_CATALOG_METHOD)); } /** Get the mongo client associated with the groups instance. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 0187de4f..8b709d5f 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -33,6 +33,7 @@ import org.bson.Document; import org.slf4j.LoggerFactory; +import com.google.common.collect.ImmutableMap; import com.mongodb.ErrorCategory; import com.mongodb.MongoException; import com.mongodb.MongoWriteException; @@ -207,19 +208,29 @@ public class MongoGroupsStorage implements GroupsStorage { /** Create MongoDB based storage for the Groups application. * @param db the MongoDB database the storage system will use. + * @param types the resource types for which indexes will be created. Querying against + * resource types not declared here will result in a table scan. Note that 4 indexes + * are created per type and 5 indexes are created automatically, which means that at most + * 14 types can be registered given MongoDBs 64 index / collection limit. * @throws StorageInitException if the storage system could not be initialized. */ - public MongoGroupsStorage(final MongoDatabase db) throws StorageInitException { - this(db, Clock.systemDefaultZone()); + public MongoGroupsStorage(final MongoDatabase db, final Collection types) + throws StorageInitException { + this(db, types, Clock.systemDefaultZone()); } // for tests - private MongoGroupsStorage(final MongoDatabase db, final Clock clock) + private MongoGroupsStorage( + final MongoDatabase db, + final Collection types, + final Clock clock) throws StorageInitException { checkNotNull(db, "db"); + checkNoNullsInCollection(types, "types"); this.db = db; this.clock = clock; - ensureIndexes(); // MUST come before check config + ensureIndexes(INDEXES); // MUST come before check config + ensureIndexes(types); checkConfig(); startExpirationAgent(EXPIRATION_AGENT_FREQUENCY_SEC); } @@ -320,11 +331,12 @@ private void checkConfig() throws StorageInitException { } } - private void ensureIndexes() throws StorageInitException { - for (final String col: INDEXES.keySet()) { - for (final List idx: INDEXES.get(col).keySet()) { + private void ensureIndexes(Map, IndexOptions>> indexes) + throws StorageInitException { + for (final String col: indexes.keySet()) { + for (final List idx: indexes.get(col).keySet()) { final Document index = new Document(); - final IndexOptions opts = INDEXES.get(col).get(idx); + final IndexOptions opts = indexes.get(col).get(idx); for (final String field: idx) { index.put(field, 1); } @@ -342,7 +354,29 @@ private void ensureIndexes() throws StorageInitException { } } } - + private void ensureIndexes(final Collection types) throws StorageInitException { + final Map, IndexOptions> groups = new HashMap<>(); + for (final ResourceType t: types) { + final String resourceIDField = Fields.GROUP_RESOURCES + Fields.FIELD_SEP + t.getName() + + Fields.FIELD_SEP + Fields.GROUP_RESOURCE_ID; + // find by resource ID & owner and sort by ID + groups.put(Arrays.asList(resourceIDField, Fields.GROUP_OWNER, Fields.GROUP_ID), null); + // find by resource ID & admin and sort by ID + groups.put(Arrays.asList(resourceIDField, Fields.GROUP_ADMINS, Fields.GROUP_ID), null); + // find public groups by resource ID and sort by ID (not needed?) + groups.put(Arrays.asList(resourceIDField, Fields.GROUP_IS_PRIVATE, Fields.GROUP_ID), + null); + // find by resource ID & member and sort by ID + groups.put(Arrays.asList( + resourceIDField, + Fields.GROUP_MEMBERS + Fields.FIELD_SEP + Fields.GROUP_MEMBER_NAME, + Fields.GROUP_ID), + null); + } + + ensureIndexes(ImmutableMap.of(COL_GROUPS, groups)); + } + private static class DuplicateKeyExceptionChecker { // might need this stuff later, so keeping for now. diff --git a/src/us/kbase/test/groups/MongoStorageTestManager.java b/src/us/kbase/test/groups/MongoStorageTestManager.java index c6e4df74..f28116c4 100644 --- a/src/us/kbase/test/groups/MongoStorageTestManager.java +++ b/src/us/kbase/test/groups/MongoStorageTestManager.java @@ -6,6 +6,8 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.time.Clock; +import java.util.Collection; +import java.util.Collections; import org.bson.Document; @@ -75,8 +77,8 @@ public void reset() throws Exception { TestCommon.destroyDB(db); clockMock = mock(Clock.class); final Constructor con = MongoGroupsStorage.class. - getDeclaredConstructor(MongoDatabase.class, Clock.class); + getDeclaredConstructor(MongoDatabase.class, Collection.class, Clock.class); con.setAccessible(true); - storage = con.newInstance(db, clockMock); + storage = con.newInstance(db, Collections.emptySet(), clockMock); } } diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java index afd017e1..92bfaf7c 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java @@ -7,6 +7,7 @@ import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @@ -29,6 +30,7 @@ import us.kbase.groups.core.GroupName; import us.kbase.groups.core.GroupUser; import us.kbase.groups.core.UserName; +import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.storage.exceptions.StorageInitException; import us.kbase.groups.storage.mongo.MongoGroupsStorage; import us.kbase.test.groups.MongoStorageTestManager; @@ -55,20 +57,18 @@ public void clearDB() throws Exception { manager.reset(); } + @Test - public void nullConstructor() throws Exception { - try { - new MongoGroupsStorage(null); - fail("expected exception"); - } catch (NullPointerException e) { - assertThat("incorrect exception message", e.getMessage(), is("db")); - } + public void failConstructNulls() throws Exception { + failMongoStart(null, set(), new NullPointerException("db")); + failMongoStart(manager.db, null, new NullPointerException("types")); + failMongoStart(manager.db, set(new ResourceType("t"), null), new NullPointerException( + "Null item in collection types")); } - @Test public void startUpAndCheckConfigDoc() throws Exception { final MongoDatabase db = manager.mc.getDatabase("startUpAndCheckConfigDoc"); - new MongoGroupsStorage(db); + new MongoGroupsStorage(db, set()); final MongoCollection col = db.getCollection("config"); assertThat("Only one config doc", col.countDocuments(), is(1L)); final FindIterable c = col.find(); @@ -79,7 +79,7 @@ public void startUpAndCheckConfigDoc() throws Exception { assertThat("schema v1", (Integer)d.get("schemaver"), is(1)); //check startup works with the config object in place - final MongoGroupsStorage ms = new MongoGroupsStorage(db); + final MongoGroupsStorage ms = new MongoGroupsStorage(db, set()); final GroupUser u = GroupUser.getBuilder(new UserName("u"), Instant.ofEpochMilli(10000)) .build(); @@ -111,7 +111,7 @@ public void startUpWith2ConfigDocs() throws Exception { "startUpWith2ConfigDocs.config( index: |\\.\\$)schema_1\\s+dup key: " + "\\{ : \"schema\" \\}'"); try { - new MongoGroupsStorage(db); + new MongoGroupsStorage(db, set()); fail("started mongo with bad config"); } catch (StorageInitException e) { final Matcher match = errorPattern.matcher(e.getMessage()); @@ -148,10 +148,16 @@ public void startUpWithUpdateInProgress() throws Exception { "schema. Aborting startup.")); } - private void failMongoStart(final MongoDatabase db, final Exception exp) - throws Exception { + private void failMongoStart(final MongoDatabase db, final Exception exp) throws Exception { + failMongoStart(db, set(), exp); + } + + private void failMongoStart( + final MongoDatabase db, + final Collection types, + final Exception exp) throws Exception { try { - new MongoGroupsStorage(db); + new MongoGroupsStorage(db, types); fail("started mongo with bad config"); } catch (Exception e) { TestCommon.assertExceptionCorrect(e, exp); @@ -203,8 +209,12 @@ public void indexesGroups() { final Set indexes = new HashSet<>(); manager.db.getCollection("groups").listIndexes() .forEach((Consumer) indexes::add); + assertThat("incorrect indexes", indexes, is(getDefaultGroupsIndexes())); + } + + private Set getDefaultGroupsIndexes() { final String col = "test_mongogroupsstorage.groups"; - assertThat("incorrect indexes", indexes, is(set( + return set( new Document("v", manager.indexVer) .append("unique", true) .append("key", new Document("id", 1)) @@ -230,7 +240,57 @@ public void indexesGroups() { .append("key", new Document("memb.user", 1).append("id", 1)) .append("name", "memb.user_1_id_1") .append("ns", col) - ))); + ); + } + + //TODO NOW check indexes are getting used + @Test + public void indexesGroupsWithResourceTypes() throws Exception { + manager.db.drop(); + try { + new MongoGroupsStorage( + manager.db, Arrays.asList(new ResourceType("t1"), new ResourceType("t2"))); + final Set indexes = new HashSet<>(); + manager.db.getCollection("groups").listIndexes() + .forEach((Consumer) indexes::add); + + final String col = "test_mongogroupsstorage.groups"; + final Set expected = getDefaultGroupsIndexes(); + for (final String t: set("t1", "t2")) { + final String resKey = "resources." + t + ".rid"; + expected.addAll(set( + new Document("v", manager.indexVer) + .append("key", new Document(resKey, 1) + .append("own", 1) + .append("id", 1)) + .append("name", resKey + "_1_own_1_id_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document(resKey, 1) + .append("admin", 1) + .append("id", 1)) + .append("name", resKey + "_1_admin_1_id_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document(resKey, 1) + .append("priv", 1) + .append("id", 1)) + .append("name", resKey + "_1_priv_1_id_1") + .append("ns", col), + new Document("v", manager.indexVer) + .append("key", new Document(resKey, 1) + .append("memb.user", 1) + .append("id", 1)) + .append("name", resKey + "_1_memb.user_1_id_1") + .append("ns", col) + + )); + } + assertThat("incorrect indexes", indexes, is(expected)); + } finally { + manager.db.drop(); + } + } @Test From 55830dbe5d91ad478b69d57b5151a6cc3a5078e8 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Fri, 15 Mar 2019 12:48:42 -0700 Subject: [PATCH 29/39] Add filter groups by resource to mongo layer Changed indexes since MongoDB won't index 2 arrays in the same compound index, and doesn't tell you that until you try to index something --- RELEASE_NOTES.md | 3 + src/us/kbase/groups/core/Groups.java | 2 +- .../kbase/groups/storage/GroupsStorage.java | 7 +- .../storage/mongo/MongoGroupsStorage.java | 53 ++-- .../test/groups/MongoStorageTestManager.java | 7 +- src/us/kbase/test/groups/core/GroupsTest.java | 11 +- .../mongo/MongoGroupsStorageOpsTest.java | 235 +++++++++++++++--- .../mongo/MongoGroupsStorageStartupTest.java | 10 +- 8 files changed, 266 insertions(+), 62 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 617ba5f7..1851af31 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,9 @@ * The `group` collection `own_1` and `admin_1` MongoDB indexes have been replaced by `own_1_id_1` and `admin_1_id_1` indexes and can be deleted. +* In total, 14 new MongoDB indexes have been added to support the new features in this version. + As such, the first server startup on existing data will take extra time as the indexes are + built. ### Release notes diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index fac257c1..0c2ce110 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -551,7 +551,7 @@ public List getGroups(final Token userToken, final GetGroupsParams pa throw new UnauthorizedException("A token is required when filtering groups by role"); } final UserName user = getOptionalUser(userToken); - return storage.getGroups(params, user).stream() + return storage.getGroups(params, false, user).stream() //TODO NOW check resource .map(g -> toMinimalView(user, g)).collect(Collectors.toList()); } diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index 228de792..213f9c13 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -117,12 +117,17 @@ List getGroupNames(UserName user, Collection gro * At most 100 groups are returned. * If the user is null and the user's role is not {@link Group.Role#NONE} no groups * are returned. + * If the user is null, a resource is present in the parameters, and that resource is private, + * no groups are returned. * @param params the parameters for getting the groups. + * @param resourceIsPublic true if the resource in the params is public, false otherwise. + * Ignored if no resource is present. * @param user an optional user. If no user is provided, only public groups are returned. * @return the groups. * @throws GroupsStorageException if an error occurs contacting the storage system. */ - List getGroups(GetGroupsParams params, UserName user) throws GroupsStorageException; + List getGroups(GetGroupsParams params, boolean resourceIsPublic, UserName user) + throws GroupsStorageException; /** Add a member to a group. * @param groupID the ID of the group. diff --git a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java index 8b709d5f..3fb241e4 100644 --- a/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java +++ b/src/us/kbase/groups/storage/mongo/MongoGroupsStorage.java @@ -209,9 +209,9 @@ public class MongoGroupsStorage implements GroupsStorage { /** Create MongoDB based storage for the Groups application. * @param db the MongoDB database the storage system will use. * @param types the resource types for which indexes will be created. Querying against - * resource types not declared here will result in a table scan. Note that 4 indexes + * resource types not declared here will result in a table scan. Note that 3 indexes * are created per type and 5 indexes are created automatically, which means that at most - * 14 types can be registered given MongoDBs 64 index / collection limit. + * 19 types can be registered given MongoDBs 64 index / collection limit. * @throws StorageInitException if the storage system could not be initialized. */ public MongoGroupsStorage(final MongoDatabase db, final Collection types) @@ -357,21 +357,21 @@ private void ensureIndexes(Map, IndexOptions>> indexes) private void ensureIndexes(final Collection types) throws StorageInitException { final Map, IndexOptions> groups = new HashMap<>(); for (final ResourceType t: types) { + /* Note admins and members cannot be indexed here because mongo will not allow + * a compound index including more than one array. + * Hence we add a general resource ID index and allow Mongo to select the best index + * to use when doing user + resource ID look ups (and it might use both and combine + * the results). + */ final String resourceIDField = Fields.GROUP_RESOURCES + Fields.FIELD_SEP + t.getName() + Fields.FIELD_SEP + Fields.GROUP_RESOURCE_ID; + // find by resource ID and sort by ID + groups.put(Arrays.asList(resourceIDField, Fields.GROUP_ID), null); // find by resource ID & owner and sort by ID groups.put(Arrays.asList(resourceIDField, Fields.GROUP_OWNER, Fields.GROUP_ID), null); - // find by resource ID & admin and sort by ID - groups.put(Arrays.asList(resourceIDField, Fields.GROUP_ADMINS, Fields.GROUP_ID), null); // find public groups by resource ID and sort by ID (not needed?) groups.put(Arrays.asList(resourceIDField, Fields.GROUP_IS_PRIVATE, Fields.GROUP_ID), null); - // find by resource ID & member and sort by ID - groups.put(Arrays.asList( - resourceIDField, - Fields.GROUP_MEMBERS + Fields.FIELD_SEP + Fields.GROUP_MEMBER_NAME, - Fields.GROUP_ID), - null); } ensureIndexes(ImmutableMap.of(COL_GROUPS, groups)); @@ -779,10 +779,15 @@ private List getList( } @Override - public List getGroups(final GetGroupsParams params, final UserName user) + public List getGroups( + final GetGroupsParams params, + final boolean resourceIsPublic, + final UserName user) throws GroupsStorageException { + // ugh. This method is not pretty. requireNonNull(params, "params"); - if (user == null && !params.getRole().equals(Role.NONE)) { // TODO NOW test in Groups and throw error + if (user == null && (!params.getRole().equals(Role.NONE) || + (params.getResourceType().isPresent() && !resourceIsPublic))) { return Collections.emptyList(); } final Document query = new Document(); @@ -792,12 +797,18 @@ public List getGroups(final GetGroupsParams params, final UserName user) } final String memberField = Fields.GROUP_MEMBERS + Fields.FIELD_SEP + Fields.GROUP_MEMBER_NAME; + appendResourceInPlace(params, query); // resource is public if present && user == null if (user == null) { query.append(Fields.GROUP_IS_PRIVATE, false); } else if (params.getRole().equals(Role.NONE)) { - query.append("$or", Arrays.asList(new Document(Fields.GROUP_IS_PRIVATE, false), - // members contains all members - new Document(memberField, user.getName()))); + // members array contains all members + final Document memberQuery = new Document(memberField, user.getName()); + if (resourceIsPublic || !params.getResourceType().isPresent()) { + final Document pubquery = new Document(Fields.GROUP_IS_PRIVATE, false); + query.append("$or", Arrays.asList(pubquery, memberQuery)); + } else { + query.putAll(memberQuery); + } } else if (params.getRole().equals(Role.OWNER)) { query.append(Fields.GROUP_OWNER, user.getName()); } else if (params.getRole().equals(Role.ADMIN)) { @@ -806,7 +817,7 @@ public List getGroups(final GetGroupsParams params, final UserName user) new Document(Fields.GROUP_OWNER, user.getName()) )); } else { - // members contains all members + // members array contains all members query.append(memberField, user.getName()); } // may want to allow alternate sorts later, will need indexes @@ -815,6 +826,16 @@ public List getGroups(final GetGroupsParams params, final UserName user) return getList(COL_GROUPS, query, new Document(), sort, 100, d -> toGroup(d)); } + private Document appendResourceInPlace(final GetGroupsParams params, final Document query) { + if (params.getResourceType().isPresent()) { + final String resourceKey = Fields.GROUP_RESOURCES + Fields.FIELD_SEP + + params.getResourceType().get().getName() + Fields.FIELD_SEP + + Fields.GROUP_RESOURCE_ID; + query.append(resourceKey, params.getResourceID().get().getName()); + } + return query; + } + private Group toGroup(final Document grp) throws GroupsStorageException { try { final Map members = getMembers(grp); diff --git a/src/us/kbase/test/groups/MongoStorageTestManager.java b/src/us/kbase/test/groups/MongoStorageTestManager.java index f28116c4..36d42b36 100644 --- a/src/us/kbase/test/groups/MongoStorageTestManager.java +++ b/src/us/kbase/test/groups/MongoStorageTestManager.java @@ -16,6 +16,7 @@ import com.mongodb.client.MongoDatabase; import us.kbase.common.test.controllers.mongo.MongoController; +import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.storage.mongo.MongoGroupsStorage; public class MongoStorageTestManager { @@ -65,6 +66,10 @@ public void destroy() throws Exception { } public void reset() throws Exception { + reset(Collections.emptySet()); + } + + public void reset(Collection types) throws Exception { if (storage != null) { // not sure this will fix the occasional test errors due to too many expires // running during the test, but worth a try. Maybe some of the previously @@ -79,6 +84,6 @@ public void reset() throws Exception { final Constructor con = MongoGroupsStorage.class. getDeclaredConstructor(MongoDatabase.class, Collection.class, Clock.class); con.setAccessible(true); - storage = con.newInstance(db, Collections.emptySet(), clockMock); + storage = con.newInstance(db, types, clockMock); } } diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index d81fc6be..7097b263 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -1539,6 +1539,7 @@ public void getGroupsEmpty() throws Exception { .withNullableExcludeUpTo("ex") .withNullableSortAscending(false) .build(), + false, null)) .thenReturn(Collections.emptyList()); @@ -1556,6 +1557,7 @@ public void getGroups() throws Exception { when(mocks.storage.getGroups(GetGroupsParams.getBuilder() .withNullableExcludeUpTo("someex") .build(), + false, null)) .thenReturn(Arrays.asList( Group.getBuilder( @@ -1636,7 +1638,8 @@ public void getGroupsWithCustomFields() throws Exception { .withCustomField(new NumberedCustomField("missingmin"), "missingonmin") .withCustomField(new NumberedCustomField("missingpub"), "missingonpub") .build(); - when(mocks.storage.getGroups(eq(mtparams), any())).thenReturn(Arrays.asList(grp)); + when(mocks.storage.getGroups(eq(mtparams), eq(false), any())) + .thenReturn(Arrays.asList(grp)); when(mocks.storage.getGroups(Arrays.asList(new GroupID("id1")))).thenReturn(set(grp)); when(mocks.validators.getConfigOrEmpty(new CustomField("minpub"))).thenReturn( @@ -1771,8 +1774,8 @@ public void getGroupsWithPrivateGroup() throws Exception { .build(); when(mocks.userHandler.getUser(new Token("t1"))).thenReturn(new UserName("m1")); - when(mocks.storage.getGroups(ggp, null)).thenReturn(Arrays.asList(g1, g3)); - when(mocks.storage.getGroups(ggp, new UserName("m1"))) + when(mocks.storage.getGroups(ggp, false, null)).thenReturn(Arrays.asList(g1, g3)); + when(mocks.storage.getGroups(ggp, false, new UserName("m1"))) .thenReturn(Arrays.asList(g1, g2, g3)); assertThat("incorrect groups", mocks.groups.getGroups(null, ggp), @@ -1796,7 +1799,7 @@ public void getGroupsWithRole() throws Exception { .build(); when(mocks.userHandler.getUser(new Token("t1"))).thenReturn(new UserName("m1")); - when(mocks.storage.getGroups(any(), eq(new UserName("m1")))) + when(mocks.storage.getGroups(any(), eq(false), eq(new UserName("m1")))) .thenReturn(Arrays.asList(g1)); for (final Role r: Role.values()) { diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index 83ab3053..f452b2c7 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -106,7 +106,7 @@ public static void tearDownClass() throws Exception { @Before public void before() throws Exception { - manager.reset(); + manager.reset(set(new ResourceType("workspace"))); logEvents.clear(); } @@ -1187,16 +1187,20 @@ private void failUpdateGroup( @Test public void getGroupsNoUserWithRole() throws Exception { + manager.storage.createGroup(Group.getBuilder( + new GroupID("gid"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .build()); assertThat("incorrect get groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), null), + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), false, null), is(Collections.emptyList())); } @Test public void getGroupsEmpty() throws Exception { assertThat("incorrect get groups", - manager.storage.getGroups(GetGroupsParams.getBuilder().build(), null), + manager.storage.getGroups(GetGroupsParams.getBuilder().build(), false, null), is(Collections.emptyList())); } @@ -1236,7 +1240,7 @@ public void getGroupsDefaultParams() throws Exception { .build()); assertThat("incorrect get group", manager.storage.getGroups( - GetGroupsParams.getBuilder().build(), null), is(Arrays.asList( + GetGroupsParams.getBuilder().build(), false, null), is(Arrays.asList( Group.getBuilder(new GroupID("aid"), new GroupName("name1"), GroupUser.getBuilder(new UserName("uname1"), inst(12000)) .withCustomField( @@ -1309,55 +1313,226 @@ public void getGroupsWithUserRole() throws Exception { manager.storage.createGroup(g4); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().build(), null), + GetGroupsParams.getBuilder().build(), false, null), is(Arrays.asList(g2, g3, g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.NONE).build(), null), + GetGroupsParams.getBuilder().withRole(Role.NONE).build(), false, null), is(Arrays.asList(g2, g3, g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.NONE).build(), new UserName("a1")), + GetGroupsParams.getBuilder().withRole(Role.NONE).build(), + false, new UserName("a1")), is(Arrays.asList(g1, g2, g3, g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.NONE).build(), new UserName("m")), + GetGroupsParams.getBuilder().withRole(Role.NONE).build(), + false, new UserName("m")), is(Arrays.asList(g2, g3, g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("a1")), + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), + false, new UserName("a1")), is(Arrays.asList(g1, g2, g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("m")), + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), + false, new UserName("m")), is(Arrays.asList(g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("o")), + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), + false, new UserName("o")), is(Arrays.asList(g1, g4))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), new UserName("v")), + GetGroupsParams.getBuilder().withRole(Role.MEMBER).build(), + false, new UserName("v")), is(Collections.emptyList())); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("a1")), + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), + false, new UserName("a1")), is(Arrays.asList(g1, g2))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("m1")), + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), + false, new UserName("m1")), is(Arrays.asList(g2))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("m")), + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), + false, new UserName("m")), is(Collections.emptyList())); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), new UserName("o")), + GetGroupsParams.getBuilder().withRole(Role.ADMIN).build(), + false, new UserName("o")), is(Arrays.asList(g1))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("m1")), + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), + false, new UserName("m1")), is(Collections.emptyList())); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("a1")), + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), + false, new UserName("a1")), is(Arrays.asList(g2))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("o")), + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), + false, new UserName("o")), is(Arrays.asList(g1))); assertThat("incorrect groups", manager.storage.getGroups( - GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), new UserName("o1")), + GetGroupsParams.getBuilder().withRole(Role.OWNER).build(), + false, new UserName("o1")), is(Arrays.asList(g3, g4))); } + @Test + public void getGroupsWithResourceNoUser() throws Exception { + final Group g1 = Group.getBuilder( + new GroupID("gid"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + manager.storage.createGroup(g1); + + // private group, invisible + final Group g2 = Group.getBuilder( + new GroupID("gid2"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .withIsPrivate(true) + .build(); + manager.storage.createGroup(g2); + + // different resource + final Group g3 = Group.getBuilder( + new GroupID("gid3"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("6"))) + .build(); + manager.storage.createGroup(g3); + + // private resource + assertThat("incorrect get groups", + manager.storage.getGroups( + GetGroupsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("7")) + .build(), + false, null), + is(Collections.emptyList())); + + // public resource + assertThat("incorrect get groups", + manager.storage.getGroups( + GetGroupsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("7")) + .build(), + true, null), + is(Arrays.asList(g1))); + } + + @Test + public void getGroupsWithResourceWithUserWithRoles() throws Exception { + final Group g1 = Group.getBuilder( + new GroupID("gid"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withMember(toGUser("mem")) + .withIsPrivate(true) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + final Group g2 = Group.getBuilder( + new GroupID("gid2"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(60000))) + .withAdministrator(toGUser("mem")) + .withIsPrivate(true) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + final Group g3 = Group.getBuilder( + new GroupID("gid3"), new GroupName("name3"), toGUser("mem"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(70000))) + .withIsPrivate(true) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + final Group priv = Group.getBuilder( + new GroupID("priv"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(80000))) + .withIsPrivate(true) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + final Group pub = Group.getBuilder( + new GroupID("pub"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(90000))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + final Group diffrespub = Group.getBuilder( + new GroupID("diffrespub"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(90000))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("6"))) + .build(); + final Group diffresmem = Group.getBuilder( + new GroupID("diffresmem"), new GroupName("name3"), toGUser("mem"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(90000))) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("6"))) + .build(); + + manager.storage.createGroup(priv); + manager.storage.createGroup(g1); + manager.storage.createGroup(diffrespub); + manager.storage.createGroup(g2); + manager.storage.createGroup(pub); + manager.storage.createGroup(diffresmem); + manager.storage.createGroup(g3); + + final GetGroupsParams.Builder p = GetGroupsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("7")); + + // no role, private resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), false, new UserName("mem")), + is(Arrays.asList(g1, g2, g3))); + + // no role, public resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), true, new UserName("mem")), + is(Arrays.asList(g1, g2, g3, pub))); + + p.withRole(Role.MEMBER); + + // member, private resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), false, new UserName("mem")), + is(Arrays.asList(g1, g2, g3))); + + // member, public resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), true, new UserName("mem")), + is(Arrays.asList(g1, g2, g3))); + + p.withRole(Role.ADMIN); + + // admin, private resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), false, new UserName("mem")), + is(Arrays.asList(g2, g3))); + + // admin, public resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), true, new UserName("mem")), + is(Arrays.asList(g2, g3))); + + p.withRole(Role.OWNER); + + // owner, private resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), false, new UserName("mem")), + is(Arrays.asList(g3))); + + // owner, public resource + assertThat("incorrect get groups", + manager.storage.getGroups(p.build(), true, new UserName("mem")), + is(Arrays.asList(g3))); + } + @Test public void getGroupsPublicAndPrivate() throws Exception { final Group g1 = Group.getBuilder( @@ -1394,13 +1569,13 @@ public void getGroupsPublicAndPrivate() throws Exception { final GetGroupsParams p = GetGroupsParams.getBuilder().build(); - assertThat("incorrect groups", manager.storage.getGroups(p, null), + assertThat("incorrect groups", manager.storage.getGroups(p, false, null), is(Arrays.asList(g2))); - assertThat("incorrect groups", manager.storage.getGroups(p, new UserName("o")), + assertThat("incorrect groups", manager.storage.getGroups(p, false, new UserName("o")), is(Arrays.asList(g1, g2))); - assertThat("incorrect groups", manager.storage.getGroups(p, new UserName("a")), + assertThat("incorrect groups", manager.storage.getGroups(p, false, new UserName("a")), is(Arrays.asList(g2, g3))); - assertThat("incorrect groups", manager.storage.getGroups(p, new UserName("m")), + assertThat("incorrect groups", manager.storage.getGroups(p, false, new UserName("m")), is(Arrays.asList(g2, g4))); } @@ -1492,9 +1667,9 @@ public void getGroupsLimitWithPrivateGroups() throws Exception { manager.storage.createGroup(b.build()); } final List resNull = manager.storage.getGroups(GetGroupsParams.getBuilder() - .withNullableExcludeUpTo("g010").build(), null); + .withNullableExcludeUpTo("g010").build(), false, null); final List resNonMember = manager.storage.getGroups(GetGroupsParams.getBuilder() - .withNullableExcludeUpTo("g010").build(), new UserName("nonmember")); + .withNullableExcludeUpTo("g010").build(), false, new UserName("nonmember")); final List expected = new LinkedList<>(); for (int i = 11; i < 210; i += 2) { @@ -1513,7 +1688,7 @@ public void getGroupsLimitWithPrivateGroups() throws Exception { } final List res2 = manager.storage.getGroups(GetGroupsParams.getBuilder() - .withNullableExcludeUpTo("g010").build(), new UserName("m")); + .withNullableExcludeUpTo("g010").build(), false, new UserName("m")); final List expected2 = new LinkedList<>(); for (int i = 10; i < 110; i += 1) { @@ -1536,7 +1711,7 @@ private void assertGroupListCorrect( final int size) throws Exception { final List res = manager.storage.getGroups(GetGroupsParams.getBuilder() - .withNullableExcludeUpTo(excludeUpTo).build(), null); + .withNullableExcludeUpTo(excludeUpTo).build(), false, null); assertThat("incorrect size", res.size(), is(size)); int i = start; for (final Group g: res) { @@ -1549,13 +1724,13 @@ private void assertGroupListCorrect( private void checkGroupsList(final GetGroupsParams p, final List expected) throws GroupsStorageException { - assertThat("incorrect groups", manager.storage.getGroups(p, null), is(expected)); + assertThat("incorrect groups", manager.storage.getGroups(p, false, null), is(expected)); } @Test public void getGroupsFail() throws Exception { try { - manager.storage.getGroups(null, new UserName("foo")); + manager.storage.getGroups(null, false, new UserName("foo")); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, new NullPointerException("params")); diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java index 92bfaf7c..bee6fb9c 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageStartupTest.java @@ -267,23 +267,15 @@ public void indexesGroupsWithResourceTypes() throws Exception { .append("ns", col), new Document("v", manager.indexVer) .append("key", new Document(resKey, 1) - .append("admin", 1) .append("id", 1)) - .append("name", resKey + "_1_admin_1_id_1") + .append("name", resKey + "_1_id_1") .append("ns", col), new Document("v", manager.indexVer) .append("key", new Document(resKey, 1) .append("priv", 1) .append("id", 1)) .append("name", resKey + "_1_priv_1_id_1") - .append("ns", col), - new Document("v", manager.indexVer) - .append("key", new Document(resKey, 1) - .append("memb.user", 1) - .append("id", 1)) - .append("name", resKey + "_1_memb.user_1_id_1") .append("ns", col) - )); } assertThat("incorrect indexes", indexes, is(expected)); From 2a6181cd5abece3015126b9f6f659a9ce10e3968 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Fri, 15 Mar 2019 12:54:11 -0700 Subject: [PATCH 30/39] Check list groups by resource discriminates on resource type --- .../mongo/MongoGroupsStorageOpsTest.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java index f452b2c7..2bd0756d 100644 --- a/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java +++ b/src/us/kbase/test/groups/storage/mongo/MongoGroupsStorageOpsTest.java @@ -106,7 +106,7 @@ public static void tearDownClass() throws Exception { @Before public void before() throws Exception { - manager.reset(set(new ResourceType("workspace"))); + manager.reset(set(new ResourceType("workspace"), new ResourceType("catalogmethod"))); logEvents.clear(); } @@ -1405,6 +1405,15 @@ public void getGroupsWithResourceNoUser() throws Exception { .build(); manager.storage.createGroup(g3); + // different resource type + final Group g4 = Group.getBuilder( + new GroupID("gid4"), new GroupName("name3"), toGUser("uname3"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(50000))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + manager.storage.createGroup(g4); + // private resource assertThat("incorrect get groups", manager.storage.getGroups( @@ -1474,7 +1483,14 @@ public void getGroupsWithResourceWithUserWithRoles() throws Exception { .withResource(new ResourceType("workspace"), new ResourceDescriptor(new ResourceID("6"))) .build(); + final Group difftypemem = Group.getBuilder( + new GroupID("difftypemem"), new GroupName("name3"), toGUser("mem"), + new CreateAndModTimes(Instant.ofEpochMilli(40000), Instant.ofEpochMilli(1000000))) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceID("7"))) + .build(); + manager.storage.createGroup(difftypemem); manager.storage.createGroup(priv); manager.storage.createGroup(g1); manager.storage.createGroup(diffrespub); From a653b176297e73853067b086f7683f53a5df48ad Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Fri, 15 Mar 2019 15:55:04 -0700 Subject: [PATCH 31/39] Add isPublic(resource) method to resource handlers Will be needed for listing groups by resource, since whether the resource is public or not determines whether it's visible to users --- .../SDKClientCatalogHandler.java | 8 +- .../groups/core/resource/ResourceHandler.java | 11 ++ .../kbase/groups/storage/GroupsStorage.java | 3 +- .../SDKClientWorkspaceHandler.java | 56 +++++++--- .../SDKClientCatalogHandlerTest.java | 26 ++++- .../SDKClientWorkspaceHandlerTest.java | 102 ++++++++++++++++++ 6 files changed, 188 insertions(+), 18 deletions(-) diff --git a/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java b/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java index 08612377..bf27c9f6 100644 --- a/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java +++ b/src/us/kbase/groups/cataloghandler/SDKClientCatalogHandler.java @@ -104,6 +104,12 @@ public boolean isAdministrator(final ResourceID resource, final UserName user) checkNotNull(user, "user"); return getModuleOwners(resource).contains(user.getName()); } + + @Override + public boolean isPublic(final ResourceID resource) throws IllegalResourceIDException { + getModMeth(resource); // check format + return true; + } private List getModuleOwners(final ResourceID module) throws ResourceHandlerException, NoSuchResourceException, IllegalResourceIDException { @@ -235,7 +241,7 @@ private Set filterNonAdministrated( } @Override - public void setReadPermission(ResourceID resource, UserName user) { + public void setReadPermission(final ResourceID resource, final UserName user) { return; // nothing to do, catalog methods are all public } } diff --git a/src/us/kbase/groups/core/resource/ResourceHandler.java b/src/us/kbase/groups/core/resource/ResourceHandler.java index 5df40d27..997fbd15 100644 --- a/src/us/kbase/groups/core/resource/ResourceHandler.java +++ b/src/us/kbase/groups/core/resource/ResourceHandler.java @@ -36,6 +36,17 @@ ResourceDescriptor getDescriptor(ResourceID resource) boolean isAdministrator(ResourceID resource, UserName user) throws IllegalResourceIDException, ResourceHandlerException, NoSuchResourceException; + /** Check if a resource is public. The resource handler implementation may or may not + * check for the existence of the resource if all resources are public. + * @param resource the resource to check. + * @return true if the resource is public, false otherwise. + * @throws IllegalResourceIDException if the resource ID is not in a legal format. + * @throws ResourceHandlerException if an error occurs contacting the resource service. + * @throws NoSuchResourceException if there is no such resource. + */ + boolean isPublic(ResourceID resource) + throws IllegalResourceIDException, ResourceHandlerException, NoSuchResourceException; + /** Get the set of administrators of a resource. * @param resource the resource. * @return the set of administrators of that resource. diff --git a/src/us/kbase/groups/storage/GroupsStorage.java b/src/us/kbase/groups/storage/GroupsStorage.java index 213f9c13..05fe511b 100644 --- a/src/us/kbase/groups/storage/GroupsStorage.java +++ b/src/us/kbase/groups/storage/GroupsStorage.java @@ -115,8 +115,7 @@ List getGroupNames(UserName user, Collection gro /** Get groups in the system, sorted by the group ID. * At most 100 groups are returned. - * If the user is null and the user's role is not {@link Group.Role#NONE} no groups - * are returned. + * If the user is null and the user's role is not None no groups are returned. * If the user is null, a resource is present in the parameters, and that resource is private, * no groups are returned. * @param params the parameters for getting the groups. diff --git a/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java b/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java index bca25071..092ea83b 100644 --- a/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java +++ b/src/us/kbase/groups/workspacehandler/SDKClientWorkspaceHandler.java @@ -12,6 +12,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -60,6 +61,7 @@ public class SDKClientWorkspaceHandler implements ResourceHandler { private static final String PERM_WRITE = "w"; private static final String PERM_READ = "r"; private static final String GLOBAL_READ_USER = "*"; + private static final String PUBLIC = "public"; private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern( "yyyy'-'MM'-'dd'T'HH':'mm':'ssX"); @@ -108,12 +110,19 @@ private long getWSID(final ResourceID id) throws IllegalResourceIDException { @Override public boolean isAdministrator(final ResourceID resource, final UserName user) throws NoSuchResourceException, IllegalResourceIDException, ResourceHandlerException { - checkNotNull(resource, "resource"); - checkNotNull(user, "user"); + requireNonNull(resource, "resource"); + requireNonNull(user, "user"); final Perms perms = getPermissions(Arrays.asList(getWSID(resource)), true); return new Perm(user, perms.perms.get(0)).perm.isAdmin(); } + @Override + public boolean isPublic(final ResourceID resource) + throws IllegalResourceIDException, ResourceHandlerException, NoSuchResourceException { + requireNonNull(resource, "resource"); + return (boolean) getWSInfo(getWSID(resource), false, true).wi.get(PUBLIC); + } + private final TypeReference>>> TR_GET_PERMS = new TypeReference>>>() {}; @@ -214,7 +223,12 @@ public ResourceInformationSet getResourceInformation( } else { final Perm perm = new Perm(user, perms.perms.get(0)); if (hasAccess(perm, access)) { - final WSInfoOwner wi = getWSInfo(wsid); + final WSInfoOwner wi; + try { + wi = getWSInfo(wsid, true, false); + } catch (NoSuchResourceException e) { + throw new RuntimeException("Shouldn't be possible", e); + } if (wi == null) { // should almost never happen since we checked for inaccessible ws above b.withNonexistentResource(rid); @@ -263,23 +277,37 @@ private WSInfoOwner(Map wi, String owner) { } // returns null if missing or deleted - private WSInfoOwner getWSInfo(final long wsid) throws ResourceHandlerException { + private WSInfoOwner getWSInfo( + final long wsid, + boolean withDescriptionAndNarrativeInfo, + boolean throwNoWorkspaceException) + throws ResourceHandlerException, NoSuchResourceException { final Tuple9> wsinfo; final String desc; - final NarrInfo narrInfo; + final Optional narrInfo; try { final WorkspaceIdentity wsi = new WorkspaceIdentity().withId((long) wsid); wsinfo = client.administer(new UObject(ImmutableMap.of( "command", "getWorkspaceInfo", "params", wsi))) .asClassInstance(WS_INFO_TYPEREF); - final UObject d = client.administer(new UObject(ImmutableMap.of( - "command", "getWorkspaceDescription", "params", wsi))); - desc = d == null ? null : d.asScalar(); - narrInfo = getNarrativeInfo(wsinfo.getE1(), wsinfo.getE9()); + if (withDescriptionAndNarrativeInfo) { + final UObject d = client.administer(new UObject(ImmutableMap.of( + "command", "getWorkspaceDescription", "params", wsi))); + desc = d == null ? null : d.asScalar(); + narrInfo = Optional.of(getNarrativeInfo(wsinfo.getE1(), wsinfo.getE9())); + } else { + desc = null; + narrInfo = Optional.empty(); + } } catch (ServerException e) { - if (getWorkspaceID(e) != null) { // deleted or missing - return null; + final Integer errorid = getWorkspaceID(e); + if (errorid != null) { // deleted or missing + if (throwNoWorkspaceException) { + throw new NoSuchResourceException(errorid + "", e); + } else { + return null; + } } else { throw getGeneralWSException(e); } @@ -288,9 +316,9 @@ private WSInfoOwner getWSInfo(final long wsid) throws ResourceHandlerException { } final Map ret = new HashMap<>(); ret.put("name", wsinfo.getE2()); - ret.put("narrname", narrInfo.name); - ret.put("narrcreate", narrInfo.created); - ret.put("public", PERM_READ.equals(wsinfo.getE7())); + ret.put("narrname", narrInfo.map(n -> n.name).orElse(null)); + ret.put("narrcreate", narrInfo.map(n -> n.created).orElse(null)); + ret.put(PUBLIC, PERM_READ.equals(wsinfo.getE7())); ret.put("moddate", timestampToEpochMS(wsinfo.getE4())); ret.put("description", desc); return new WSInfoOwner(ret, wsinfo.getE3()); diff --git a/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java b/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java index 826d6752..352592d7 100644 --- a/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java +++ b/src/us/kbase/test/groups/cataloghandler/SDKClientCatalogHandlerTest.java @@ -188,7 +188,7 @@ private void isAdministrator( .isAdministrator(new ResourceID("modname.mod"), new UserName(name)), is(expected)); } - public ModuleVersionInfo getMVI(final List narrs, final List locals) { + private ModuleVersionInfo getMVI(final List narrs, final List locals) { final ModuleVersionInfo mvi = new ModuleVersionInfo(); mvi.setAdditionalProperties("local_functions", locals); mvi.setAdditionalProperties("narrative_methods", narrs); @@ -276,6 +276,30 @@ private void failIsAdministrator( } } + @Test + public void isPublic() throws Exception { + final SDKClientCatalogHandler cli = new SDKClientCatalogHandler(mock(CatalogClient.class)); + + assertThat("incorrect is public", cli.isPublic(new ResourceID("foo.bar")), is(true)); + assertThat("incorrect is public", cli.isPublic(new ResourceID("DataFileUtil.get_objects")), + is(true)); + } + + @Test + public void isPublicFailBadID() throws Exception { + final SDKClientCatalogHandler cli = new SDKClientCatalogHandler(mock(CatalogClient.class)); + + for (final String mm: BAD_NAMES.keySet()) { + try { + cli.isPublic(new ResourceID(mm)); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, new IllegalResourceIDException( + "Illegal catalog method name: " + BAD_NAMES.get(mm))); + } + } + } + @Test public void getAdministratorsEmpty() throws Exception { getAdministrators(Collections.emptyList(), set(), diff --git a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java index adc2376f..44aa1767 100644 --- a/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java +++ b/src/us/kbase/test/groups/workspacehandler/SDKClientWorkspaceHandlerTest.java @@ -3,12 +3,14 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import static us.kbase.test.groups.TestCommon.set; @@ -276,6 +278,106 @@ private void failIsAdmin( } } + @Test + public void isPublicFalse() throws Exception { + isPublic(false); + } + + @Test + public void isPublicTrue() throws Exception { + isPublic(true); + } + + private void isPublic(final boolean pub) throws Exception { + final WorkspaceClient c = mock(WorkspaceClient.class); + + when(c.ver()).thenReturn(MIN_WS_VER); + + final SDKClientWorkspaceHandler h = new SDKClientWorkspaceHandler(c); + + doReturn(getWorkspaceInfoResponse(3, "name3", "user1", "2018-10-27T02:16:53+0000", pub, + Collections.emptyMap())) + .when(c).administer(argThat(getWSInfoCommandMatcher(3))); + + assertThat("incorrect is public", h.isPublic(new ResourceID("3")), is(pub)); + + verify(c, times(2)).administer(any()); // admin() is called in the constructor + } + + @Test + public void isPublicFailBadArgs() throws Exception { + final WorkspaceClient c = mock(WorkspaceClient.class); + + when(c.ver()).thenReturn(MIN_WS_VER); + + final SDKClientWorkspaceHandler h = new SDKClientWorkspaceHandler(c); + + failIsPublic(h, null, new NullPointerException("resource")); + failIsPublic(h, new ResourceID("yay"), new IllegalResourceIDException("yay")); + } + + @Test + public void isPublicFailDeletedWS() throws Exception { + isPublicFail(new ServerException("Workspace 24 is deleted", -1, "n"), + new NoSuchResourceException("24")); + } + + @Test + public void isPublicFailMissingWS() throws Exception { + isPublicFail(new ServerException("No workspace with id 24 exists", -1, "n"), + new NoSuchResourceException("24")); + } + + @Test + public void isPublicFailOtherServerException() throws Exception { + isPublicFail(new ServerException("You pootied real bad I can smell it", -1, "n"), + new ResourceHandlerException("Error contacting workspace at http://foo.com")); + } + + @Test + public void isPublicFailJsonClientException() throws Exception { + isPublicFail(new JsonClientException("You pootied real bad I can smell it"), + new ResourceHandlerException("Error contacting workspace at http://foo.com")); + } + + @Test + public void isPublicFailIOException() throws Exception { + isPublicFail(new IOException("You pootied real bad I can smell it"), + new ResourceHandlerException("Error contacting workspace at http://foo.com")); + } + + @Test + public void isPublicFailIllegalStateException() throws Exception { + isPublicFail(new IllegalStateException("You pootied real bad I can smell it"), + new ResourceHandlerException("Error contacting workspace at http://foo.com")); + } + + private void isPublicFail(final Exception exception, final Exception expected) + throws Exception { + final WorkspaceClient c = mock(WorkspaceClient.class); + + when(c.ver()).thenReturn(MIN_WS_VER); + when(c.getURL()).thenReturn(new URL("http://foo.com")); + + final SDKClientWorkspaceHandler h = new SDKClientWorkspaceHandler(c); + + when(c.administer(argThat(getWSInfoCommandMatcher(3)))).thenThrow(exception); + + failIsPublic(h, new ResourceID("3"), expected); + } + + private void failIsPublic( + final SDKClientWorkspaceHandler h, + final ResourceID id, + final Exception expected) { + try { + h.isPublic(id); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test public void getResourceInformationNoWS() throws Exception { final WorkspaceClient c = mock(WorkspaceClient.class); From 4e2d48e59d16f5c4586dc6f6f08edf1a4a3e946a Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Fri, 15 Mar 2019 16:44:52 -0700 Subject: [PATCH 32/39] Add resource filtering for groups to core code --- src/us/kbase/groups/core/Groups.java | 16 +++++++- .../kbase/groups/service/api/GroupsAPI.java | 3 +- src/us/kbase/test/groups/core/GroupsTest.java | 40 +++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index 0c2ce110..c437fe9b 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -535,6 +535,8 @@ public Map groupsHaveRequests( /** Get minimal views of the groups in the system. * At most 100 groups are returned. + * If the token is null, a resource is present in the parameters, and that resource is private, + * no groups are returned. * @param userToken the user's token. If null, only public groups are returned. * @param params the parameters for getting the groups. * @return the groups. @@ -542,16 +544,26 @@ public Map groupsHaveRequests( * @throws InvalidTokenException if the token is invalid. * @throws AuthenticationException if authentication fails. * @throws UnauthorizedException if a role is specified but no token is provided. + * @throws ResourceHandlerException if an error occurs contacting the resource service. + * @throws NoSuchResourceTypeException if the specified resource type does not exist. + * @throws IllegalResourceIDException if the specified resource id is illegal. + * @throws NoSuchResourceException if the specified resource does not exist. */ public List getGroups(final Token userToken, final GetGroupsParams params) throws GroupsStorageException, InvalidTokenException, AuthenticationException, - UnauthorizedException { + UnauthorizedException, NoSuchResourceException, IllegalResourceIDException, + NoSuchResourceTypeException, ResourceHandlerException { checkNotNull(params, "params"); if (userToken == null && !params.getRole().equals(Role.NONE)) { throw new UnauthorizedException("A token is required when filtering groups by role"); } final UserName user = getOptionalUser(userToken); - return storage.getGroups(params, false, user).stream() //TODO NOW check resource + boolean resourceIsPublic = false; + if (params.getResourceType().isPresent()) { + resourceIsPublic = getHandler(params.getResourceType().get()) + .isPublic(params.getResourceID().get()); + } + return storage.getGroups(params, resourceIsPublic, user).stream() .map(g -> toMinimalView(user, g)).collect(Collectors.toList()); } diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index 07f522e3..a80a55b9 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -87,7 +87,8 @@ public List> getGroups( @QueryParam(Fields.GET_GROUPS_IDS) final String groupIDs) throws GroupsStorageException, IllegalParameterException, NoTokenProvidedException, InvalidTokenException, AuthenticationException, NoSuchGroupException, - UnauthorizedException { + UnauthorizedException, NoSuchResourceException, IllegalResourceIDException, + NoSuchResourceTypeException, ResourceHandlerException { final List gids = APICommon.toGroupIDs(groupIDs); final Token t = getToken(token, false); final List grps; diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 7097b263..91835c01 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -1809,6 +1809,39 @@ public void getGroupsWithRole() throws Exception { } } + @Test + public void getGroupsWithResourceTrue() throws Exception { + // checks that the resource privacy is correctly handled + getGroupsWithResource(true); + } + + @Test + public void getGroupsWithResourceFalse() throws Exception { + // checks that the resource privacy is correctly handled + getGroupsWithResource(false); + } + + private void getGroupsWithResource(final boolean resourceIsPublic) throws Exception { + final TestMocks mocks = initTestMocks(); + + final Group g1 = Group.getBuilder(new GroupID("g1"), new GroupName("n1"), + GroupUser.getBuilder(new UserName("o1"), inst(10000)).build(), + new CreateAndModTimes(inst(1000))) + .build(); + + when(mocks.userHandler.getUser(new Token("t1"))).thenReturn(new UserName("m1")); + when(mocks.wsHandler.isPublic(new ResourceID("86"))).thenReturn(resourceIsPublic); + + when(mocks.storage.getGroups(any(), eq(resourceIsPublic), eq(new UserName("m1")))) + .thenReturn(Arrays.asList(g1)); + + assertThat("incorrect groups", mocks.groups.getGroups( + new Token("t1"), GetGroupsParams.getBuilder() + .withResource(new ResourceType("workspace"), new ResourceID("86")) + .build()), + is(Arrays.asList(GroupView.getBuilder(g1, new UserName("m1")).build()))); + } + @Test public void getGroupsFailBadArgs() throws Exception { getGroupsFail(new Token("t"), null, new NullPointerException("params")); @@ -1819,6 +1852,13 @@ public void getGroupsFailBadArgs() throws Exception { } } + @Test + public void getGroupsFailNoResourceType() throws Exception { + getGroupsFail(null, GetGroupsParams.getBuilder() + .withResource(new ResourceType("t"), new ResourceID("i")).build(), + new NoSuchResourceTypeException("t")); + } + private void getGroupsFail(final Token t, final GetGroupsParams p, final Exception expected) { try { initTestMocks().groups.getGroups(t, p); From 66674f05355d74fc9f90188d589ca1b2ad5b8b82 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Fri, 15 Mar 2019 17:56:30 -0700 Subject: [PATCH 33/39] Add filter groups by resource to the groups API. --- README.md | 25 ++++++++++- RELEASE_NOTES.md | 5 ++- .../kbase/groups/service/api/APICommon.java | 41 +++++++++++++------ src/us/kbase/groups/service/api/Fields.java | 5 +++ .../kbase/groups/service/api/GroupsAPI.java | 5 ++- .../groups/service/api/APICommonTest.java | 34 ++++++++++----- .../groups/service/api/GroupsAPITest.java | 41 ++++++++++++------- 7 files changed, 114 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 665c0794..5f983c10 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,8 @@ RETURNS: ``` AUTHORIZATION OPTIONAL -GET /group[?excludeupto=&order=&role=&groupids=] +GET /group[?excludeupto=&order=&role= + &resourcetype=&resource=&groupids=] RETURNS: A list of Groups. Only the id, private, name, owner, role, memcount, rescount, custom, @@ -266,6 +267,11 @@ The query parameters are all optional: This can be used to page through the groups if needed. * `role` - Filters the group list by a minimum user role, one of `Member`, `Admin`, or `Owner`. If a role is supplied an authorization token must also be supplied. +* `resourcetype` - the type of a resource, for example `workspace`. If this parameter is + present `resource` must also be present. See below for an explanation of the effects. +* `resource` - a resource ID, for example `56` for the `workspace` resource type. If this + parameter is present, the `resourcetype` parameter must also be present. + See below for an explanation of the effects. * `groupids` - list specific groups by ID in a comma separated list (e.g. `?groupids=groupid1,groupid2,...,groupidN`). If this parameter is specified, all other parameters are ignored. This method of listing groups is much faster than getting @@ -280,6 +286,23 @@ If the user is anonymous or not a member of the group, only custom fields that a group listable (see custom fields below) are included. If the user is a member of the group, all group listable fields are included. +If a resource filter is included in the parameters (by specifying *both* `resourcetype` and +`resource`), only groups that contain that resource are included in the results. The filter +interacts with the user token and `role` parameter as follows: + +* If no token is provided: + * If the resource is a private resource, no groups are returned. + * If the resource is a public resource, only public groups containing the resource are returned. +* If a token is provided and `role` is `None`: + * If the resource is a private resource, only groups that contain the resource where the + user is a member are returned. + * If the resource is a public resource, additionally public groups containing the resource + are returned. +* If a token is provided and `role` is other than `None`, only groups containing the resource + where the user has at least the given role are returned. + +Whether the user administrates the resource or not is not currently taken into account. + ### Get group names from IDs ``` diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1851af31..a0f32888 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,11 +12,12 @@ ### Release notes +* Added the `ids`, `role`, `resourcetype`, and `resource` parameters to the `/groups` + endpoint. * Added the `/request/groups` endpoint. +* Added `resourcetype` and `resource` parameters to the four request listing endpoints. * Resource administrators can now see their resources in private groups for which they are not a member in the `/group/ endpoint`. -* Added the `ids` and `role` parameters to the `/groups` endpoint. -* Added `resourcetype` and `resource` parameters to the four request listing endpoints. ## 0.1.5 diff --git a/src/us/kbase/groups/service/api/APICommon.java b/src/us/kbase/groups/service/api/APICommon.java index a8b3e905..f8692756 100644 --- a/src/us/kbase/groups/service/api/APICommon.java +++ b/src/us/kbase/groups/service/api/APICommon.java @@ -16,6 +16,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -225,18 +226,7 @@ public static GetRequestsParams getRequestsParams( if (!isNullOrEmpty(excludeUpTo)) { b.withNullableExcludeUpTo(epochMilliStringToInstant(excludeUpTo)); } - if (isNullOrEmpty(resourceType) ^ isNullOrEmpty(resource)) { // xor - throw new IllegalParameterException("Either both or neither of the resource type " + - "and resource ID must be provided"); - } - if (!isNullOrEmpty(resourceType)) { - try { - b.withResource(new ResourceType(resourceType), new ResourceID(resource)); - } catch (MissingParameterException e) { - throw new RuntimeException( - "Reality appears to be broken. Please turn it off then on again", e); - } - } + setResource(resourceType, resource, (t, r) -> b.withResource(t, r)); setSortDirection(sortDirection, defaultSort, s -> b.withNullableSortAscending(s)); return b.withNullableIncludeClosed(includeClosed != null).build(); @@ -268,6 +258,10 @@ public static Instant epochMilliStringToInstant(final String epochMilli) * @param sortDirection the direction of the sort - 'asc' for an ascending sort, and 'desc' * for a descending sort. * @param role the minimum role the user must possess. + * @param resourceType the type of the resource that will be used to filter the list. The + * resource parameter must also be specified if this parameter is present. + * @param resource the ID of the resource that will be used to filter the list. The + * resourceType parameter must also be specified if this parameter is present. * @param defaultSort if sortDirection is null or whitespace only, this value is used instead. * true sets an ascending sort, false sets a descending sort. * @return the get groups parameters. @@ -277,6 +271,8 @@ public static GetGroupsParams getGroupsParams( final String excludeUpTo, final String sortDirection, final String role, + final String resourceType, + final String resource, final boolean defaultSort) throws IllegalParameterException { final GetGroupsParams.Builder b = GetGroupsParams.getBuilder() @@ -288,9 +284,30 @@ public static GetGroupsParams getGroupsParams( throw new IllegalParameterException(e.getMessage(), e); } } + setResource(resourceType, resource, (t, r) -> b.withResource(t, r)); setSortDirection(sortDirection, defaultSort, s -> b.withNullableSortAscending(s)); return b.build(); } + + + private static void setResource( + final String resourceType, + final String resource, + final BiConsumer resourceConsumer) + throws IllegalParameterException { + if (isNullOrEmpty(resourceType) ^ isNullOrEmpty(resource)) { // xor + throw new IllegalParameterException("Either both or neither of the resource type " + + "and resource ID must be provided"); + } + if (!isNullOrEmpty(resourceType)) { + try { + resourceConsumer.accept(new ResourceType(resourceType), new ResourceID(resource)); + } catch (MissingParameterException e) { + throw new RuntimeException( + "Reality appears to be broken. Please turn it off then on again", e); + } + } + } private static void setSortDirection( final String sortDirection, diff --git a/src/us/kbase/groups/service/api/Fields.java b/src/us/kbase/groups/service/api/Fields.java index 53d7d96d..11105a79 100644 --- a/src/us/kbase/groups/service/api/Fields.java +++ b/src/us/kbase/groups/service/api/Fields.java @@ -111,6 +111,11 @@ public class Fields { public static final String GET_GROUPS_IDS = "groupids"; /** Determine the user role to require. */ public static final String GET_GROUPS_ROLE = "role"; + /** Set the resource type with which to filter the list. */ + public static final String GET_GROUPS_RESOURCE_TYPE = "resourcetype"; + /** Set the resource ID with which to filter the list. */ + public static final String GET_GROUPS_RESOURCE_ID = "resource"; + /* *********************** * request listing fields diff --git a/src/us/kbase/groups/service/api/GroupsAPI.java b/src/us/kbase/groups/service/api/GroupsAPI.java index a80a55b9..4ed857e5 100644 --- a/src/us/kbase/groups/service/api/GroupsAPI.java +++ b/src/us/kbase/groups/service/api/GroupsAPI.java @@ -84,6 +84,8 @@ public List> getGroups( @QueryParam(Fields.GET_GROUPS_EXCLUDE_UP_TO) final String excludeUpTo, @QueryParam(Fields.GET_GROUPS_SORT_ORDER) final String order, @QueryParam(Fields.GET_GROUPS_ROLE) final String role, + @QueryParam(Fields.GET_GROUPS_RESOURCE_TYPE) final String resType, + @QueryParam(Fields.GET_GROUPS_RESOURCE_ID) final String resource, @QueryParam(Fields.GET_GROUPS_IDS) final String groupIDs) throws GroupsStorageException, IllegalParameterException, NoTokenProvidedException, InvalidTokenException, AuthenticationException, NoSuchGroupException, @@ -95,7 +97,8 @@ public List> getGroups( if (!gids.isEmpty()) { grps = groups.getGroups(t, gids); } else { - grps = groups.getGroups(t, getGroupsParams(excludeUpTo, order, role, true)); + grps = groups.getGroups(t, getGroupsParams( + excludeUpTo, order, role, resType, resource, true)); } return grps.stream().map(g -> toGroupJSON(g)).collect(Collectors.toList()); } diff --git a/src/us/kbase/test/groups/service/api/APICommonTest.java b/src/us/kbase/test/groups/service/api/APICommonTest.java index 6adcef6a..2c1f6225 100644 --- a/src/us/kbase/test/groups/service/api/APICommonTest.java +++ b/src/us/kbase/test/groups/service/api/APICommonTest.java @@ -691,11 +691,11 @@ private void failEpochMilliStringToInstant(final String em, final Exception expe @Test public void getGroupParamsNulls() throws Exception { - final GetGroupsParams p = APICommon.getGroupsParams(null, null, null, true); + final GetGroupsParams p = APICommon.getGroupsParams(null, null, null, null, null, true); assertThat("incorrect params", p, is(GetGroupsParams.getBuilder().build())); - final GetGroupsParams p2 = APICommon.getGroupsParams(null, null, null, false); + final GetGroupsParams p2 = APICommon.getGroupsParams(null, null, null, null, null, false); assertThat("incorrect params", p2, is(GetGroupsParams.getBuilder() .withNullableSortAscending(false).build())); @@ -704,11 +704,11 @@ public void getGroupParamsNulls() throws Exception { @Test public void getGroupParamsWhitespace() throws Exception { final String ws = " \t "; - final GetGroupsParams p = APICommon.getGroupsParams(ws, ws, ws, true); + final GetGroupsParams p = APICommon.getGroupsParams(ws, ws, ws, ws, ws, true); assertThat("incorrect params", p, is(GetGroupsParams.getBuilder().build())); - final GetGroupsParams p2 = APICommon.getGroupsParams(ws, ws, ws, false); + final GetGroupsParams p2 = APICommon.getGroupsParams(ws, ws, ws, ws, ws, false); assertThat("incorrect params", p2, is(GetGroupsParams.getBuilder() .withNullableSortAscending(false).build())); @@ -716,14 +716,17 @@ public void getGroupParamsWhitespace() throws Exception { @Test public void getGroupParamsValues() throws Exception { - final GetGroupsParams p = APICommon.getGroupsParams(" foo ", "asc", "Member", false); + final GetGroupsParams p = APICommon.getGroupsParams( + " foo ", "asc", "Member", "type", "id", false); assertThat("incorrect params", p, is(GetGroupsParams.getBuilder() .withRole(Role.MEMBER) .withNullableExcludeUpTo("foo") + .withResource(new ResourceType("type"), new ResourceID("id")) .build())); - final GetGroupsParams p2 = APICommon.getGroupsParams(" \t bar ", "desc", "Admin", true); + final GetGroupsParams p2 = APICommon.getGroupsParams( + " \t bar ", "desc", "Admin", null, null, true); assertThat("incorrect params", p2, is(GetGroupsParams.getBuilder() .withRole(Role.ADMIN) @@ -731,14 +734,16 @@ public void getGroupParamsValues() throws Exception { .withNullableSortAscending(false) .build())); - final GetGroupsParams p3 = APICommon.getGroupsParams(" foo ", "asc", "Owner", false); + final GetGroupsParams p3 = APICommon.getGroupsParams( + " foo ", "asc", "Owner", null, null, false); assertThat("incorrect params", p3, is(GetGroupsParams.getBuilder() .withRole(Role.OWNER) .withNullableExcludeUpTo("foo") .build())); - final GetGroupsParams p4 = APICommon.getGroupsParams(" foo ", "asc", "None", false); + final GetGroupsParams p4 = APICommon.getGroupsParams( + " foo ", "asc", "None", null, null, false); assertThat("incorrect params", p4, is(GetGroupsParams.getBuilder() .withRole(Role.NONE) @@ -748,17 +753,24 @@ public void getGroupParamsValues() throws Exception { @Test public void getGroupParamsFailBadArgs() throws Exception { - getGroupParamsFail("asd", null, new IllegalParameterException( + getGroupParamsFail("asd", null, null, null, new IllegalParameterException( "Invalid sort direction: asd")); - getGroupParamsFail(null, "member", new IllegalParameterException("Invalid role: member")); + getGroupParamsFail(null, "member", null, null, + new IllegalParameterException("Invalid role: member")); + getGroupParamsFail(null, null, "t", null, new IllegalParameterException( + "Either both or neither of the resource type and resource ID must be provided")); + getGroupParamsFail(null, null, " \t ", "r", new IllegalParameterException( + "Either both or neither of the resource type and resource ID must be provided")); } private void getGroupParamsFail( final String sort, final String role, + final String resType, + final String resource, final Exception expected) { try { - APICommon.getGroupsParams(null, sort, role, false); + APICommon.getGroupsParams(null, sort, role, resType, resource, false); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); diff --git a/src/us/kbase/test/groups/service/api/GroupsAPITest.java b/src/us/kbase/test/groups/service/api/GroupsAPITest.java index 70162715..3a54b400 100644 --- a/src/us/kbase/test/groups/service/api/GroupsAPITest.java +++ b/src/us/kbase/test/groups/service/api/GroupsAPITest.java @@ -268,28 +268,30 @@ private static Builder getGroupMaxBuilder() @Test public void getGroupsNulls() throws Exception { - getGroups(null, null, null, null, null, null, GetGroupsParams.getBuilder().build()); + getGroups(null, null, null, null, null, null, null, null, + GetGroupsParams.getBuilder().build()); } @Test public void getGroupsWhitespace() throws Exception { - getGroups(" \t ", " \t ", " \t ", " \t ", " \t ", null, - GetGroupsParams.getBuilder().build()); + final String ws = " \t "; + getGroups(ws, ws, ws, ws, ws, ws, ws, null, GetGroupsParams.getBuilder().build()); } @Test public void getGroupsWhitespaceValuesAsc() throws Exception { - getGroups(" tok \t ", " foo \t ", " asc \t ", "Member", " , \t , ", - new Token(" tok \t "), + getGroups(" tok \t ", " foo \t ", " asc \t ", "Member", + "t", "r", " , \t , ", new Token(" tok \t "), GetGroupsParams.getBuilder() .withRole(Role.MEMBER) .withNullableExcludeUpTo("foo") + .withResource(new ResourceType("t"), new ResourceID("r")) .build()); } @Test public void getGroupsWhitespaceValuesDesc() throws Exception { - getGroups("t", " foo \t ", " desc \t ", "Admin", ",", new Token("t"), + getGroups("t", " foo \t ", " desc \t ", "Admin", null, " \t ", ",", new Token("t"), GetGroupsParams.getBuilder() .withRole(Role.ADMIN) .withNullableExcludeUpTo("foo") @@ -301,6 +303,8 @@ private void getGroups( final String excludeUpTo, final String order, final String role, + final String resType, + final String resource, final String ids, // this must be null or contain ws (with commas) final Token expectedToken, final GetGroupsParams expected) @@ -314,7 +318,7 @@ private void getGroups( .withPublicUserFieldDeterminer(f -> f.getField().equals("something")) .build())); final List> ret = new GroupsAPI(g) - .getGroups(token, excludeUpTo, order, role, ids); + .getGroups(token, excludeUpTo, order, role, resType, resource, ids); assertThat("incorrect groups", ret, is(Arrays.asList(GROUP_MAX_JSON_MIN, GROUP_MIN_JSON_MIN))); @@ -353,7 +357,7 @@ private void getGroupsWithIDs( .build())); final List> ret = new GroupsAPI(g) - .getGroups(token, "id", "asc", "Owner", "id2 , priv, id "); + .getGroups(token, "id", "asc", "Owner", null, null, "id2 , priv, id "); assertThat("incorrect groups", ret, is(Arrays.asList( @@ -366,12 +370,17 @@ private void getGroupsWithIDs( public void getGroupsFailBadArgs() throws Exception { final Groups g = mock(Groups.class); - failGetGroups(g, "t", null, " asd ", null, null, new IllegalParameterException( - "Invalid sort direction: asd")); - failGetGroups(g, "t", null, null, "owner", null, new IllegalParameterException( + failGetGroups(g, "t", null, " asd ", null, null, null, null, + new IllegalParameterException("Invalid sort direction: asd")); + failGetGroups(g, "t", null, null, "owner", null, null, null, new IllegalParameterException( "Invalid role: owner")); - failGetGroups(g, "t", null, null, null, " id1 , id*bad", new IllegalParameterException( - ErrorType.ILLEGAL_GROUP_ID, "Illegal character in group id id*bad: *")); + failGetGroups(g, "t", null, null, null, null, null, " id1 , id*bad", + new IllegalParameterException(ErrorType.ILLEGAL_GROUP_ID, + "Illegal character in group id id*bad: *")); + failGetGroups(g, "t", null, null, null, "t", null, null, new IllegalParameterException( + "Either both or neither of the resource type and resource ID must be provided")); + failGetGroups(g, "t", null, null, null, " ", "r", null, new IllegalParameterException( + "Either both or neither of the resource type and resource ID must be provided")); } private void failGetGroups( @@ -380,10 +389,12 @@ private void failGetGroups( final String excludeUpTo, final String order, final String role, + final String resType, + final String resource, final String ids, final Exception expected) { try { - new GroupsAPI(g).getGroups(token, excludeUpTo, order, role, ids); + new GroupsAPI(g).getGroups(token, excludeUpTo, order, role, resType, resource, ids); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); @@ -931,7 +942,7 @@ public void getGroupWithResources() throws Exception { .thenReturn(Arrays.asList(gv.withStandardView(false).build())); final Map retmin = new GroupsAPI(g) - .getGroups("toke2", null, null, null, null).get(0); + .getGroups("toke2", null, null, null, null, null, null).get(0); final Map expectedmin = new HashMap<>(); expectedmin.putAll(GROUP_MAX_JSON_MIN); expectedmin.put("role", "Admin"); From 193f06942d3ee7a4e49b2ed4a10810b6ec1c5b53 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 19 Mar 2019 16:56:54 -0700 Subject: [PATCH 34/39] Add a core method for getting information about a resource in a request. --- README.md | 2 +- src/us/kbase/groups/core/Groups.java | 88 +++++- .../core/resource/ResourceInformation.java | 141 ++++++++++ src/us/kbase/groups/service/api/Root.java | 2 +- src/us/kbase/test/groups/core/GroupsTest.java | 258 +++++++++++++++++- .../service/api/ResourceInformationTest.java | 89 ++++++ .../test/groups/service/api/RootTest.java | 2 +- 7 files changed, 561 insertions(+), 21 deletions(-) create mode 100644 src/us/kbase/groups/core/resource/ResourceInformation.java create mode 100644 src/us/kbase/test/groups/service/api/ResourceInformationTest.java diff --git a/README.md b/README.md index 5f983c10..7466da9a 100644 --- a/README.md +++ b/README.md @@ -632,7 +632,7 @@ AUTHORIZATION REQUIRED POST /request/id//getperm ``` -The request type must be `Request`, the resource type cannot be `user`, +The request must be open and the type must be `Request`, the resource type cannot be `user`, and the user must be a group administrator. Read permissions are only granted if the user has no explicit permission to the resource and the resource is not publicly readable. diff --git a/src/us/kbase/groups/core/Groups.java b/src/us/kbase/groups/core/Groups.java index c437fe9b..ddc2272e 100644 --- a/src/us/kbase/groups/core/Groups.java +++ b/src/us/kbase/groups/core/Groups.java @@ -56,6 +56,7 @@ import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceHandler; import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceInformation; import us.kbase.groups.core.resource.ResourceInformationSet; import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.storage.GroupsStorage; @@ -1354,8 +1355,8 @@ public void removeResource( /** Set read permissions on a resource that has been requested to be added to a group * for the user if the resource is not already readable (including publicly so). The user - * must be a group administrator for the request, the request type must be - * {@link RequestType#INVITE}, and the resource type may not be "user". + * must be a group administrator for the request, the request must be open and of type + * {@link RequestType#REQUEST}, and the resource type may not be "user". * @param userToken the user's token. * @param requestID the ID of the request. * @throws NoSuchRequestException if no request with that ID exists. @@ -1373,28 +1374,91 @@ public void setReadPermission(final Token userToken, final RequestID requestID) throws NoSuchRequestException, GroupsStorageException, InvalidTokenException, AuthenticationException, UnauthorizedException, ClosedRequestException, NoSuchResourceException, IllegalResourceIDException, ResourceHandlerException { - checkNotNull(userToken, "userToken"); - checkNotNull(requestID, "requestID"); - final UserName user = userHandler.getUser(userToken); + requireNonNull(requestID, "requestID"); + final UserName user = userHandler.getUser(requireNonNull(userToken, "userToken")); + final GroupRequest r = getAndCheckRequest( + requestID, user, "resource permissions changes"); + final ResourceHandler h = getHandlerRuntimeException(r); + // catch no such resource exception and close request? Don't worry about it for now. + h.setReadPermission(r.getResource().getResourceID(), user); + } + + // check that the request is valid for the case where a group admin wants to get + // information about the resource in the request. E.g. type = REQUEST, is open, is group + // admin. + private GroupRequest getAndCheckRequest( + final RequestID requestID, + final UserName user, + final String operation) + throws NoSuchRequestException, GroupsStorageException, UnauthorizedException, + ClosedRequestException { final GroupRequest r = storage.getRequest(requestID); final Group g = getGroupFromKnownGoodRequest(r); if (!g.isAdministrator(user)) { - throw new UnauthorizedException(String.format("User %s is not an admin for group %s", - user.getName(), g.getGroupID().getName())); + throw new UnauthorizedException(String.format( + "User %s is not an admin for the group associated with request %s", + user.getName(), requestID.getID())); } if (!RequestType.REQUEST.equals(r.getType())) { - throw new UnauthorizedException( - "Only Request type requests allow for resource permissions changes."); + throw new UnauthorizedException(String.format( + "Only Request type requests allow for %s.", operation)); } if (USER_TYPE.equals(r.getResourceType())) { - throw new UnauthorizedException( - "Requests with a user resource type do not allow for permissions changes."); + throw new UnauthorizedException(String.format( + "Requests with a user resource type do not allow for %s.", operation)); } ensureIsOpen(r); + return r; + } + + /** Get information about a resource that has been requested to be added to a group. + * The user must be a group administrator for the request, the request must be open and of type + * {@link RequestType#REQUEST}, and the resource type may not be "user". + * @param userToken the user's token. + * @param requestID the ID of the request. + * @return the resource information. + * @throws NoSuchRequestException if no request with that ID exists. + * @throws InvalidTokenException if the token is invalid. + * @throws AuthenticationException if authentication fails. + * @throws GroupsStorageException if an error occurs contacting the storage system. + * @throws UnauthorizedException if the user is not an administrator of the group or the + * request type is not correct. + * @throws ClosedRequestException if the request is closed. + * @throws ResourceHandlerException if an error occurs contacting the resource service. + * @throws IllegalResourceIDException if the resource ID is illegal. + * @throws NoSuchResourceException if there is no such resource. + */ + public ResourceInformation getResourceInformation( + final Token userToken, final RequestID requestID) + throws InvalidTokenException, AuthenticationException, NoSuchRequestException, + UnauthorizedException, ClosedRequestException, GroupsStorageException, + IllegalResourceIDException, ResourceHandlerException, NoSuchResourceException { + requireNonNull(requestID, "requestID"); + final UserName user = userHandler.getUser(requireNonNull(userToken, "userToken")); + final GroupRequest r = getAndCheckRequest(requestID, user, "resource access"); final ResourceHandler h = getHandlerRuntimeException(r); - h.setReadPermission(r.getResource().getResourceID(), user); + final ResourceInformationSet riSet = h.getResourceInformation( + user, + new HashSet<>(Arrays.asList(r.getResource().getResourceID())), + ResourceAccess.ALL); + if (!riSet.getNonexistentResources().isEmpty()) { + // Close request? Don't worry about it for now. + throw new NoSuchResourceException(r.getResource().getResourceID().getName()); + } + return toResourceInformation(r.getResourceType(), riSet); } + // expects a 1 entry set + private ResourceInformation toResourceInformation( + final ResourceType resourceType, + final ResourceInformationSet riSet) { + final ResourceID rid = riSet.getResources().iterator().next(); + final ResourceInformation.Builder b = ResourceInformation.getBuilder(resourceType, rid); + riSet.getFields(rid).entrySet().stream() + .forEach(e -> b.withField(e.getKey(), e.getValue())); + return b.build(); + } + /** Set read permission on a resource for the user. * @param userToken the user's token. * @param groupID the ID of the group to be modified. diff --git a/src/us/kbase/groups/core/resource/ResourceInformation.java b/src/us/kbase/groups/core/resource/ResourceInformation.java new file mode 100644 index 00000000..e1d11675 --- /dev/null +++ b/src/us/kbase/groups/core/resource/ResourceInformation.java @@ -0,0 +1,141 @@ +package us.kbase.groups.core.resource; + +import static java.util.Objects.requireNonNull; +import static us.kbase.groups.util.Util.exceptOnEmpty; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** Information about an a resource. + * @author gaprice@lbl.gov + * + */ +public class ResourceInformation { + + private final ResourceID resourceID; + private final ResourceType resourceType; + private final Map resourceFields; + + private ResourceInformation( + final ResourceID resourceID, + final ResourceType resourceType, + final Map resourceFields) { + this.resourceID = resourceID; + this.resourceType = resourceType; + this.resourceFields = Collections.unmodifiableMap(resourceFields); + } + + /** The ID of the resource. + * @return the resource ID. + */ + public ResourceID getResourceID() { + return resourceID; + } + + /** The type of the resource. + * @return the resource type. + */ + public ResourceType getResourceType() { + return resourceType; + } + + /** Fields associated with the resource. + * The keys are guaranteed to be non-null and non-whitespace only. + * The values are unconstrained. + * @return the fields. + */ + public Map getResourceFields() { + return resourceFields; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((resourceFields == null) ? 0 : resourceFields.hashCode()); + result = prime * result + ((resourceID == null) ? 0 : resourceID.hashCode()); + result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ResourceInformation other = (ResourceInformation) obj; + if (resourceFields == null) { + if (other.resourceFields != null) { + return false; + } + } else if (!resourceFields.equals(other.resourceFields)) { + return false; + } + if (resourceID == null) { + if (other.resourceID != null) { + return false; + } + } else if (!resourceID.equals(other.resourceID)) { + return false; + } + if (resourceType == null) { + if (other.resourceType != null) { + return false; + } + } else if (!resourceType.equals(other.resourceType)) { + return false; + } + return true; + } + + /** Get a builder for a {@link ResourceInformation}. + * @param type the type of the resource. + * @param id the resource ID. + * @return the new builder. + */ + public static Builder getBuilder(final ResourceType type, final ResourceID id) { + return new Builder(type, id); + } + + /** A builder for a {@link ResourceInformation}. + * @author gaprice@lbl.gov + * + */ + public static class Builder { + + private final ResourceID resourceID; + private final ResourceType resourceType; + private final Map resourceFields = new HashMap<>(); + + private Builder(final ResourceType type, final ResourceID id) { + this.resourceType = requireNonNull(type, "type"); + this.resourceID = requireNonNull(id, "id"); + } + + /** Add a field to the builder. + * @param field the field name. Cannot be null or whitespace-only. + * @param value the value of the field. + * @return this builder. + */ + public Builder withField(final String field, final Object value) { + exceptOnEmpty(field, "field"); + resourceFields.put(field, value); + return this; + } + + /** Build the new {@link ResourceInformation}. + * @return the information. + */ + public ResourceInformation build() { + return new ResourceInformation(resourceID, resourceType, resourceFields); + } + } + +} diff --git a/src/us/kbase/groups/service/api/Root.java b/src/us/kbase/groups/service/api/Root.java index 12dc02c3..91b40423 100644 --- a/src/us/kbase/groups/service/api/Root.java +++ b/src/us/kbase/groups/service/api/Root.java @@ -26,7 +26,7 @@ public class Root { //TODO ZLATER ROOT add configurable contact email or link //TODO ZLATER swagger - private static final String VERSION = "0.1.6-dev1"; + private static final String VERSION = "0.1.6-dev2"; private static final String SERVER_NAME = "Groups service"; /** Return the root information. diff --git a/src/us/kbase/test/groups/core/GroupsTest.java b/src/us/kbase/test/groups/core/GroupsTest.java index 91835c01..d3649cea 100644 --- a/src/us/kbase/test/groups/core/GroupsTest.java +++ b/src/us/kbase/test/groups/core/GroupsTest.java @@ -91,6 +91,7 @@ import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceHandler; import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceInformation; import us.kbase.groups.core.resource.ResourceInformationSet; import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.storage.GroupsStorage; @@ -6008,7 +6009,8 @@ public void setReadPermissionResourceFailNotAdmin() throws Exception { .build()); setReadPermissionResourceFail(mocks.groups, new Token("t"), new RequestID(id), - new UnauthorizedException("User u1 is not an admin for group gid")); + new UnauthorizedException( + "User u1 is not an admin for the group associated with request " + id)); } @Test @@ -6029,7 +6031,7 @@ public void setReadPermissionResourceFailResourceType() throws Exception { b -> b.withResource(new ResourceType("user"), ResourceDescriptor.from(new UserName("u"))), new UnauthorizedException("Requests with a user resource type do not allow " + - "for permissions changes.")); + "for resource permissions changes.")); } @Test @@ -6044,6 +6046,33 @@ public void setReadPermissionResourceFailNoResourceHandler() throws Exception { id.toString())); } + @Test + public void setReadPermissionResourceFailClosed() throws Exception { + final TestMocks mocks = initTestMocks(); + final UUID id = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("t"))).thenReturn(new UserName("admin")); + when(mocks.storage.getRequest(new RequestID(id))).thenReturn( + GroupRequest.getBuilder( + new RequestID(id), new GroupID("gid"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("43"))) + .withStatus(GroupRequestStatus.denied(new UserName("d"), null)) + .build()); + when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("own"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("u1")) + .withMember(toGUser("u3")) + .withAdministrator(toGUser("admin")) + .build()); + + setReadPermissionResourceFail(mocks.groups, new Token("t"), new RequestID(id), + new ClosedRequestException(id + "")); + } + private void setReadPermissionResourceFail( final UUID id, final FuncExcept builderFn, @@ -6069,8 +6098,165 @@ private void setReadPermissionResourceFail( setReadPermissionResourceFail(mocks.groups, new Token("t"), new RequestID(id), expected); } + private void setReadPermissionResourceFail( + final Groups g, + final Token t, + final RequestID i, + final Exception expected) { + try { + g.setReadPermission(t, i); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + @Test - public void setReadPermissionResourceFailClosed() throws Exception { + public void getResourceInformationAdmin() throws Exception { + getResourceInformation(new UserName("admin")); + } + + @Test + public void getResourceInformationOwner() throws Exception { + getResourceInformation(new UserName("own")); + } + + private void getResourceInformation(final UserName user) throws Exception { + final TestMocks mocks = initTestMocks(); + final UUID id = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(user); + when(mocks.storage.getRequest(new RequestID(id))).thenReturn( + GroupRequest.getBuilder( + new RequestID(id), new GroupID("gid"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.meth"))) + .build()); + when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("own"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("u1")) + .withMember(toGUser("u3")) + .withAdministrator(toGUser("a1")) + .withAdministrator(toGUser("a3")) + .withAdministrator(toGUser("admin")) + .build()); + + when(mocks.catHandler.getResourceInformation( + user, set(new ResourceID("m.meth")), ResourceAccess.ALL)) + .thenReturn(ResourceInformationSet.getBuilder(user) + .withResourceField(new ResourceID("m.meth"), "f1", 1) + .withResourceField(new ResourceID("m.meth"), "f2", "cat has no fields") + .build()); + + final ResourceInformation ret = mocks.groups.getResourceInformation( + new Token("token"), new RequestID(id)); + + assertThat("incorrect info", ret, is(ResourceInformation.getBuilder( + new ResourceType("catalogmethod"), new ResourceID("m.meth")) + .withField("f1", 1) + .withField("f2", "cat has no fields") + .build())); + } + + @Test + public void getResourceInformationFailNulls() throws Exception { + final Groups g = initTestMocks().groups; + + getResourceInformationFail(g, null, new RequestID(UUID.randomUUID()), + new NullPointerException("userToken")); + getResourceInformationFail(g, new Token("t"), null, + new NullPointerException("requestID")); + } + + @Test + public void getResourceInformationFailFailNoGroup() throws Exception { + final TestMocks mocks = initTestMocks(); + final UUID id = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("t"))).thenReturn(new UserName("u1")); + when(mocks.storage.getRequest(new RequestID(id))).thenReturn( + GroupRequest.getBuilder( + new RequestID(id), new GroupID("gid"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("43"))) + .build()); + when(mocks.storage.getGroup(new GroupID("gid"))) + .thenThrow(new NoSuchGroupException("gid")); + + getResourceInformationFail(mocks.groups, new Token("t"), new RequestID(id), + new RuntimeException(String.format( + "Request %s's group doesn't exist: 50000 No such group: gid", + id.toString()))); + } + + @Test + public void getResourceInformationFailNotAdmin() throws Exception { + final TestMocks mocks = initTestMocks(); + final UUID id = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("t"))).thenReturn(new UserName("u1")); + when(mocks.storage.getRequest(new RequestID(id))).thenReturn( + GroupRequest.getBuilder( + new RequestID(id), new GroupID("gid"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("43"))) + .build()); + when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("own"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("u1")) + .withMember(toGUser("u3")) + .withAdministrator(toGUser("admin")) + .build()); + + getResourceInformationFail(mocks.groups, new Token("t"), new RequestID(id), + new UnauthorizedException( + "User u1 is not an admin for the group associated with request " + id)); + } + + @Test + public void getResourceInformationFailInvite() throws Exception { + getResourceInformationFail( + UUID.randomUUID(), + b -> b.withType(RequestType.INVITE) + .withResource(new ResourceType("workspace"), + new ResourceDescriptor(new ResourceID("2"))), + new UnauthorizedException( + "Only Request type requests allow for resource access.")); + } + + @Test + public void getResourceInformationFailResourceType() throws Exception { + getResourceInformationFail( + UUID.randomUUID(), + b -> b.withResource(new ResourceType("user"), + ResourceDescriptor.from(new UserName("u"))), + new UnauthorizedException("Requests with a user resource type do not allow " + + "for resource access.")); + } + + @Test + public void getResourceInformationFailNoResourceHandler() throws Exception { + final UUID id = UUID.randomUUID(); + getResourceInformationFail( + id, + b -> b.withResource(new ResourceType("wrkspce"), + new ResourceDescriptor(new ResourceID("3"))), + new RuntimeException( + "No handler configured for resource type wrkspce in request " + + id.toString())); + } + + @Test + public void getResourceInformationFailClosed() throws Exception { final TestMocks mocks = initTestMocks(); final UUID id = UUID.randomUUID(); @@ -6092,17 +6278,77 @@ public void setReadPermissionResourceFailClosed() throws Exception { .withAdministrator(toGUser("admin")) .build()); - setReadPermissionResourceFail(mocks.groups, new Token("t"), new RequestID(id), + getResourceInformationFail(mocks.groups, new Token("t"), new RequestID(id), new ClosedRequestException(id + "")); } - private void setReadPermissionResourceFail( + @Test + public void getResourceInformationFailNoSuchResource() throws Exception { + final TestMocks mocks = initTestMocks(); + final UUID id = UUID.randomUUID(); + + when(mocks.userHandler.getUser(new Token("token"))).thenReturn(new UserName("a1")); + when(mocks.storage.getRequest(new RequestID(id))).thenReturn( + GroupRequest.getBuilder( + new RequestID(id), new GroupID("gid"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build()) + .withResource(new ResourceType("catalogmethod"), + new ResourceDescriptor(new ResourceAdministrativeID("m"), + new ResourceID("m.meth"))) + .build()); + when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("own"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("u1")) + .withMember(toGUser("u3")) + .withAdministrator(toGUser("a1")) + .withAdministrator(toGUser("a3")) + .withAdministrator(toGUser("admin")) + .build()); + + when(mocks.catHandler.getResourceInformation( + new UserName("a1"), set(new ResourceID("m.meth")), ResourceAccess.ALL)) + .thenReturn(ResourceInformationSet.getBuilder(new UserName("a1")) + .withNonexistentResource(new ResourceID("m.meth")) + .build()); + + getResourceInformationFail(mocks.groups, new Token("token"), new RequestID(id), + new NoSuchResourceException("m.meth")); + } + + private void getResourceInformationFail( + final UUID id, + final FuncExcept builderFn, + final Exception expected) + throws Exception { + final TestMocks mocks = initTestMocks(); + + when(mocks.userHandler.getUser(new Token("t"))).thenReturn(new UserName("own")); + when(mocks.storage.getRequest(new RequestID(id))).thenReturn( + builderFn.apply(GroupRequest.getBuilder( + new RequestID(id), new GroupID("gid"), new UserName("user"), + CreateModAndExpireTimes.getBuilder( + Instant.ofEpochMilli(10000), Instant.ofEpochMilli(20000)).build())) + .build()); + when(mocks.storage.getGroup(new GroupID("gid"))).thenReturn(Group.getBuilder( + new GroupID("gid"), new GroupName("name"), toGUser("own"), + new CreateAndModTimes(Instant.ofEpochMilli(10000))) + .withMember(toGUser("u1")) + .withMember(toGUser("u3")) + .withAdministrator(toGUser("admin")) + .build()); + + getResourceInformationFail(mocks.groups, new Token("t"), new RequestID(id), expected); + } + + private void getResourceInformationFail( final Groups g, final Token t, final RequestID i, final Exception expected) { try { - g.setReadPermission(t, i); + g.getResourceInformation(t, i); fail("expected exception"); } catch (Exception got) { TestCommon.assertExceptionCorrect(got, expected); diff --git a/src/us/kbase/test/groups/service/api/ResourceInformationTest.java b/src/us/kbase/test/groups/service/api/ResourceInformationTest.java new file mode 100644 index 00000000..f17a87a5 --- /dev/null +++ b/src/us/kbase/test/groups/service/api/ResourceInformationTest.java @@ -0,0 +1,89 @@ +package us.kbase.test.groups.service.api; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.util.Collections; + +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; +import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceInformation; +import us.kbase.groups.core.resource.ResourceType; +import us.kbase.test.auth2.MapBuilder; +import us.kbase.test.groups.TestCommon; + +public class ResourceInformationTest { + + @Test + public void equals() throws Exception { + EqualsVerifier.forClass(ResourceInformation.class).usingGetClass().verify(); + } + + @Test + public void buildMinimal() throws Exception { + final ResourceInformation ri = ResourceInformation.getBuilder( + new ResourceType("t"), new ResourceID("i")) + .build(); + + assertThat("incorrect type", ri.getResourceType(), is(new ResourceType("t"))); + assertThat("incorrect id", ri.getResourceID(), is(new ResourceID("i"))); + assertThat("incorrect fields", ri.getResourceFields(), is(Collections.emptyMap())); + } + + @Test + public void buildMaximal() throws Exception { + final ResourceInformation ri = ResourceInformation.getBuilder( + new ResourceType("t"), new ResourceID("i")) + .withField("f", null) + .withField("foo", "bar") + .withField("baz", 1) + .withField("whee", " \t ") + .build(); + + assertThat("incorrect type", ri.getResourceType(), is(new ResourceType("t"))); + assertThat("incorrect id", ri.getResourceID(), is(new ResourceID("i"))); + assertThat("incorrect fields", ri.getResourceFields(), is(MapBuilder.newHashMap() + .with("f", null).with("foo", "bar").with("baz", 1).with("whee", " \t ") + .build())); + } + + @Test + public void getBuilderFailNulls() throws Exception { + getBuilderFail(null, new ResourceID("i"), new NullPointerException("type")); + getBuilderFail(new ResourceType("t"), null, new NullPointerException("id")); + } + + private void getBuilderFail( + final ResourceType t, + final ResourceID i, + final Exception expected) { + try { + ResourceInformation.getBuilder(t, i); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + + @Test + public void withFieldFailBadInput() throws Exception { + withFieldFail(null, new IllegalArgumentException( + "field cannot be null or whitespace only")); + withFieldFail(" \t ", new IllegalArgumentException( + "field cannot be null or whitespace only")); + } + + private void withFieldFail(final String field, final Exception expected) { + try { + ResourceInformation.getBuilder(new ResourceType("t"), new ResourceID("i")) + .withField(field, null); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + +} diff --git a/src/us/kbase/test/groups/service/api/RootTest.java b/src/us/kbase/test/groups/service/api/RootTest.java index f0acda92..4cf89164 100644 --- a/src/us/kbase/test/groups/service/api/RootTest.java +++ b/src/us/kbase/test/groups/service/api/RootTest.java @@ -17,7 +17,7 @@ public class RootTest { - public static final String SERVER_VER = "0.1.6-dev1"; + public static final String SERVER_VER = "0.1.6-dev2"; private static final String GIT_ERR = "Missing git commit file gitcommit, should be in us.kbase.groups"; From 031cdf01b98c1b9893a9a6c509ce19f08fe0b7da Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 19 Mar 2019 18:03:30 -0700 Subject: [PATCH 35/39] Fix build --- build.xml | 1 + .../{service/api => core/resource}/ResourceInformationTest.java | 0 2 files changed, 1 insertion(+) rename src/us/kbase/test/groups/{service/api => core/resource}/ResourceInformationTest.java (100%) diff --git a/build.xml b/build.xml index 9ce8aeb3..a97e42f7 100644 --- a/build.xml +++ b/build.xml @@ -302,6 +302,7 @@ + diff --git a/src/us/kbase/test/groups/service/api/ResourceInformationTest.java b/src/us/kbase/test/groups/core/resource/ResourceInformationTest.java similarity index 100% rename from src/us/kbase/test/groups/service/api/ResourceInformationTest.java rename to src/us/kbase/test/groups/core/resource/ResourceInformationTest.java From 83700a028a6bf3405066b52021040748e4b537d0 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 19 Mar 2019 18:09:45 -0700 Subject: [PATCH 36/39] Fix build again --- .../test/groups/core/resource/ResourceInformationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/us/kbase/test/groups/core/resource/ResourceInformationTest.java b/src/us/kbase/test/groups/core/resource/ResourceInformationTest.java index f17a87a5..eee72a7d 100644 --- a/src/us/kbase/test/groups/core/resource/ResourceInformationTest.java +++ b/src/us/kbase/test/groups/core/resource/ResourceInformationTest.java @@ -1,4 +1,4 @@ -package us.kbase.test.groups.service.api; +package us.kbase.test.groups.core.resource; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; From d773c78958183499e8c84ad390a3d54abccd3b2f Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Tue, 19 Mar 2019 18:27:57 -0700 Subject: [PATCH 37/39] Add request -> resource info method to API --- README.md | 14 ++++- RELEASE_NOTES.md | 1 + src/us/kbase/groups/service/api/Fields.java | 3 + .../kbase/groups/service/api/RequestAPI.java | 24 ++++++++ .../groups/service/api/ServicePaths.java | 5 +- .../groups/service/api/RequestAPITest.java | 57 +++++++++++++++++++ 6 files changed, 101 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7466da9a..472b7763 100644 --- a/README.md +++ b/README.md @@ -636,6 +636,18 @@ The request must be open and the type must be `Request`, the resource type canno and the user must be a group administrator. Read permissions are only granted if the user has no explicit permission to the resource and the resource is not publicly readable. +### Get information about a resource associated with a request + +``` +AUTHORIZATION REQUIRED +GET /request/id//resource + +Returns a resource entry less the added field (see Resources above). +``` + +The request must be open and the type must be `Request`, the resource type cannot be `user`, +and the user must be a group administrator. + ### Listing requests There are four endpoints for listing requests detailed below - one for listing requests you @@ -1092,9 +1104,7 @@ see /design/*.md * Find groups where user X is an owner or admin * Find groups where user X is a member and I'm a member * Find groups that contain workspaces I administrate - * Find groups that contain workspace X and where I'm a group member * Find groups that contain catalog methods I own - * Find groups that contain catalog method X * New features * Relations between groups * This needs a lot of thought / design if the relations are hierarchical / diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a0f32888..6973d6b2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -15,6 +15,7 @@ * Added the `ids`, `role`, `resourcetype`, and `resource` parameters to the `/groups` endpoint. * Added the `/request/groups` endpoint. +* Added the `/request/id//resource` endpoint. * Added `resourcetype` and `resource` parameters to the four request listing endpoints. * Resource administrators can now see their resources in private groups for which they are not a member in the `/group/ endpoint`. diff --git a/src/us/kbase/groups/service/api/Fields.java b/src/us/kbase/groups/service/api/Fields.java index 11105a79..2e8d47a9 100644 --- a/src/us/kbase/groups/service/api/Fields.java +++ b/src/us/kbase/groups/service/api/Fields.java @@ -55,6 +55,7 @@ public class Fields { public static final String GROUP_RESOURCE_TYPE = "resourcetype"; /** The resource ID. */ public static final String GROUP_RESOURCE_ID = "rid"; + /** The date a resource was added to a group. */ public static final String GROUP_RESOURCE_ADDED = "added"; /** Whether an action is complete or not, and therefore whether a request object is @@ -95,6 +96,8 @@ public class Fields { public static final String REQUEST_LATER_THAN = "laterthan"; /** Whether a group has new requests. */ public static final String REQUEST_NEW = "new"; + /** The resource ID. */ + public static final String REQUEST_RESOURCE_ID = "rid"; /* *********************** * groups listing fields diff --git a/src/us/kbase/groups/service/api/RequestAPI.java b/src/us/kbase/groups/service/api/RequestAPI.java index 2e74dd83..d3468a3e 100644 --- a/src/us/kbase/groups/service/api/RequestAPI.java +++ b/src/us/kbase/groups/service/api/RequestAPI.java @@ -6,6 +6,7 @@ import static us.kbase.groups.service.api.APICommon.toGroupJSON; import static us.kbase.groups.service.api.APICommon.toGroupRequestJSON; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeSet; @@ -44,6 +45,7 @@ import us.kbase.groups.core.exceptions.UserIsMemberException; import us.kbase.groups.core.request.GroupRequestWithActions; import us.kbase.groups.core.request.RequestID; +import us.kbase.groups.core.resource.ResourceInformation; import us.kbase.groups.storage.exceptions.GroupsStorageException; @Path(ServicePaths.REQUEST) @@ -102,6 +104,28 @@ public void getPerms( groups.setReadPermission(getToken(token, true), new RequestID(requestID)); } + @GET + @Path(ServicePaths.REQUEST_ID_RESOURCE) + @Produces(MediaType.APPLICATION_JSON) + public Map getResourceInformation( + @HeaderParam(HEADER_TOKEN) final String token, + @PathParam(Fields.REQUEST_ID) final String requestID) + throws NoSuchRequestException, InvalidTokenException, NoTokenProvidedException, + AuthenticationException, UnauthorizedException, IllegalParameterException, + MissingParameterException, GroupsStorageException, ClosedRequestException, + NoSuchResourceException, IllegalResourceIDException, ResourceHandlerException { + return toResourceInfo( + groups.getResourceInformation(getToken(token, true), new RequestID(requestID))); + } + + private Map toResourceInfo(final ResourceInformation resourceInformation) { + final Map ret = new HashMap<>(); + ret.putAll(resourceInformation.getResourceFields()); + ret.put(Fields.REQUEST_RESOURCE_ID, resourceInformation.getResourceID().getName()); + ret.put(Fields.REQUEST_RESOURCE_TYPE, resourceInformation.getResourceType().getName()); + return ret; + } + @GET @Path(ServicePaths.REQUEST_CREATED) @Produces(MediaType.APPLICATION_JSON) diff --git a/src/us/kbase/groups/service/api/ServicePaths.java b/src/us/kbase/groups/service/api/ServicePaths.java index d2ef4890..f3843660 100644 --- a/src/us/kbase/groups/service/api/ServicePaths.java +++ b/src/us/kbase/groups/service/api/ServicePaths.java @@ -9,6 +9,7 @@ public class ServicePaths { private static final String UPDATE = "update"; private static final String GETPERM = "getperm"; private static final String GROUP_STR = "group"; + private static final String RESOURCE = "resource"; /* general strings */ @@ -45,7 +46,7 @@ public class ServicePaths { public static final String GROUP_USER_ID_ADMIN = GROUP_USER_ID + SEP + "admin"; // resources - private static final String GROUP_RESOURCE = SEP + "resource" + SEP ; + private static final String GROUP_RESOURCE = SEP + RESOURCE + SEP ; /** The location to add or remove a resource from a group. */ public static final String GROUP_RESOURCE_ID = GROUP_ID + GROUP_RESOURCE + "{" + Fields.GROUP_RESOURCE_TYPE + "}" + SEP + "{" + Fields.GROUP_RESOURCE_ID + "}"; @@ -64,6 +65,8 @@ public class ServicePaths { /** The location to request permissions to view an outside resource associated with a * request. */ public static final String REQUEST_ID_PERMS = REQUEST_ID + SEP + GETPERM; + /** The location to get information about an outside resource associated with a request. */ + public static final String REQUEST_ID_RESOURCE = REQUEST_ID + SEP + RESOURCE; /** The location to cancel a request. */ public static final String REQUEST_CANCEL = REQUEST_ID + SEP + "cancel"; /** The location to accept a request. */ diff --git a/src/us/kbase/test/groups/service/api/RequestAPITest.java b/src/us/kbase/test/groups/service/api/RequestAPITest.java index 429f91c7..77964482 100644 --- a/src/us/kbase/test/groups/service/api/RequestAPITest.java +++ b/src/us/kbase/test/groups/service/api/RequestAPITest.java @@ -50,6 +50,7 @@ import us.kbase.groups.core.request.RequestType; import us.kbase.groups.core.resource.ResourceDescriptor; import us.kbase.groups.core.resource.ResourceID; +import us.kbase.groups.core.resource.ResourceInformation; import us.kbase.groups.core.resource.ResourceType; import us.kbase.groups.service.api.RequestAPI; import us.kbase.groups.service.api.RequestAPI.DenyRequestJSON; @@ -344,6 +345,62 @@ public void failGetPerms( } } + @Test + public void getResourceInformation() throws Exception { + final Groups g = mock(Groups.class); + + final String id = UUID.randomUUID().toString(); + + when(g.getResourceInformation(new Token("token"), new RequestID(id))) + .thenReturn(ResourceInformation.getBuilder( + new ResourceType("t"), new ResourceID("i")) + .withField("rid", 78) // expect overwrite + .withField("resourcetype", 89) // expect overwrite + .withField("f1", 90) + .withField("f2", "foo") + .build()); + + final Map ret = new RequestAPI(g).getResourceInformation("token", id); + + assertThat("incorrect fields", ret, is(MapBuilder.newHashMap() + .with("rid", "i") + .with("resourcetype", "t") + .with("f1", 90) + .with("f2", "foo") + .build())); + } + + @Test + public void getResourceInformationFailMissingInput() throws Exception { + final Groups g = mock(Groups.class); + + final String id = UUID.randomUUID().toString(); + + getResourceInformationFail(g, null, id, + new NoTokenProvidedException("No token provided")); + getResourceInformationFail(g, " \t ", id, + new NoTokenProvidedException("No token provided")); + getResourceInformationFail(g, "t", null, + new MissingParameterException("request id")); + getResourceInformationFail(g, "t", " \t ", + new MissingParameterException("request id")); + getResourceInformationFail(g, "t", "foo", + new IllegalParameterException("foo is not a valid request id")); + } + + public void getResourceInformationFail( + final Groups g, + final String token, + final String requestID, + final Exception expected) { + try { + new RequestAPI(g).getResourceInformation(token, requestID); + fail("expected exception"); + } catch (Exception got) { + TestCommon.assertExceptionCorrect(got, expected); + } + } + // not really sure how to name these other than copy the params. @Test public void getCreatedRequests1() throws Exception { From 37b57d1cba2eb6c6586ab8d7667b77b3b620cde0 Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Wed, 20 Mar 2019 11:14:23 -0700 Subject: [PATCH 38/39] minor doc fix --- README.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 472b7763..6f080d65 100644 --- a/README.md +++ b/README.md @@ -625,28 +625,33 @@ group a value of approximately 14Gy BCE is assumed and thus in most cases groups Each group is mapped to a further mapping to allow for backwards-compatible expansion of the API in the future. -### Get permission to read a resource associated with a request +### Get information about a resource associated with a request ``` AUTHORIZATION REQUIRED -POST /request/id//getperm +GET /request/id//resource + +RETURNS: a resource entry (but see below). ``` +Resource entries are described in `Resources` above. The resource entry returned here is slightly +different: a) there is no `added` field because presumably the resource has not yet been added +to the group, and b) there is an additional `resourcetype` field that specifies the type +of the resource. + The request must be open and the type must be `Request`, the resource type cannot be `user`, -and the user must be a group administrator. Read permissions are only granted if the user -has no explicit permission to the resource and the resource is not publicly readable. +and the user must be a group administrator. -### Get information about a resource associated with a request +### Get permission to read a resource associated with a request ``` AUTHORIZATION REQUIRED -GET /request/id//resource - -Returns a resource entry less the added field (see Resources above). +POST /request/id//getperm ``` The request must be open and the type must be `Request`, the resource type cannot be `user`, -and the user must be a group administrator. +and the user must be a group administrator. Read permissions are only granted if the user +has no explicit permission to the resource and the resource is not publicly readable. ### Listing requests From acecda821750af51506ff7268e3a9dbb97ffca3a Mon Sep 17 00:00:00 2001 From: Gavin Price Date: Fri, 22 Mar 2019 12:27:18 -0700 Subject: [PATCH 39/39] Bump version --- src/us/kbase/groups/service/api/Root.java | 2 +- src/us/kbase/test/groups/service/api/RootTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/us/kbase/groups/service/api/Root.java b/src/us/kbase/groups/service/api/Root.java index 91b40423..eb476c4d 100644 --- a/src/us/kbase/groups/service/api/Root.java +++ b/src/us/kbase/groups/service/api/Root.java @@ -26,7 +26,7 @@ public class Root { //TODO ZLATER ROOT add configurable contact email or link //TODO ZLATER swagger - private static final String VERSION = "0.1.6-dev2"; + private static final String VERSION = "0.1.6"; private static final String SERVER_NAME = "Groups service"; /** Return the root information. diff --git a/src/us/kbase/test/groups/service/api/RootTest.java b/src/us/kbase/test/groups/service/api/RootTest.java index 4cf89164..7414cf61 100644 --- a/src/us/kbase/test/groups/service/api/RootTest.java +++ b/src/us/kbase/test/groups/service/api/RootTest.java @@ -17,7 +17,7 @@ public class RootTest { - public static final String SERVER_VER = "0.1.6-dev2"; + public static final String SERVER_VER = "0.1.6"; private static final String GIT_ERR = "Missing git commit file gitcommit, should be in us.kbase.groups";