Extend group search attribute functionality to account for use case where only the leaf group is required

This commit is contained in:
Alice Wood 2023-01-23 16:33:41 -05:00 committed by Pedro Igor
parent 9bc30f4705
commit 7e56938b74
4 changed files with 42 additions and 8 deletions

View file

@ -146,4 +146,8 @@ public interface GroupsResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
List<GroupRepresentation> query(@QueryParam("q") String searchQuery);
@GET
@Produces(MediaType.APPLICATION_JSON)
List<GroupRepresentation> query(@QueryParam("q") String searchQuery, @QueryParam("populateHierarchy") boolean populateHierarchy);
}

View file

@ -150,8 +150,10 @@ public class ModelToRepresentation {
return rep;
}
public static Stream<GroupRepresentation> searchGroupsByAttributes(KeycloakSession session, RealmModel realm, boolean full, Map<String,String> attributes, Integer first, Integer max) {
return session.groups().searchGroupsByAttributes(realm, attributes, first, max)
public static Stream<GroupRepresentation> searchGroupsByAttributes(KeycloakSession session, RealmModel realm, boolean full, boolean populateHierarchy, Map<String,String> attributes, Integer first, Integer max) {
Stream<GroupModel> groups = session.groups().searchGroupsByAttributes(realm, attributes, first, max);
if(populateHierarchy) {
groups = groups
// We need to return whole group hierarchy when any child group fulfills the attribute search,
// therefore for each group from the result, we need to find root group
.map(group -> {
@ -162,10 +164,10 @@ public class ModelToRepresentation {
})
// More child groups of one root can fulfill the search, so we need to filter duplicates
.filter(StreamsUtil.distinctByKey(GroupModel::getId))
// and then turn the result into GroupRepresentations creating whole hierarchy of child groups for each root group
.map(g -> toGroupHierarchy(g, full, attributes));
.filter(StreamsUtil.distinctByKey(GroupModel::getId));
}
// and then turn the result into GroupRepresentations creating whole hierarchy of child groups for each root group
return groups.map(g -> toGroupHierarchy(g, full, attributes));
}
@Deprecated

View file

@ -79,12 +79,13 @@ public class GroupsResource {
@QueryParam("exact") @DefaultValue("false") Boolean exact,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation,
@QueryParam("populateHierarchy") @DefaultValue("true") boolean populateHierarchy) {
auth.groups().requireList();
if (Objects.nonNull(searchQuery)) {
Map<String, String> attributes = SearchQueryUtils.getFields(searchQuery);
return ModelToRepresentation.searchGroupsByAttributes(session, realm, !briefRepresentation, attributes, firstResult, maxResults);
return ModelToRepresentation.searchGroupsByAttributes(session, realm, !briefRepresentation, populateHierarchy, attributes, firstResult, maxResults);
} else if (Objects.nonNull(search)) {
return ModelToRepresentation.searchForGroupByName(session, realm, !briefRepresentation, search.trim(), exact, firstResult, maxResults);
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {

View file

@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import java.util.Arrays;
@ -143,11 +144,37 @@ public class GroupSearchTest extends AbstractGroupTest {
}
}
@Test
public void testNestedGroupQuerySearchNoHierarchy() throws Exception {
configureSearchableAttributes(ATTR_URL_NAME, ATTR_ORG_NAME, ATTR_QUOTES_NAME);
try (Creator<GroupResource> parentGroupCreator = Creator.create(testRealmResource(), parentGroup)) {
parentGroupCreator.resource().subGroup(childGroup);
GroupRepresentation testGroup = new GroupRepresentation();
testGroup.setName("test_child");
parentGroupCreator.resource().subGroup(testGroup);
// query for the child group by org name
GroupsResource search = testRealmResource().groups();
String searchQuery = String.format("%s:%s", ATTR_ORG_NAME, "childOrg");
List<GroupRepresentation> found = search.query(searchQuery, false);
assertThat(found.size(), is(1));
assertThat(found.get(0).getName(), is(equalTo(CHILD_GROUP)));
String path = found.get(0).getPath();
assertThat(path, is(String.format("/%s/%s", PARENT_GROUP, CHILD_GROUP)));
} finally {
resetSearchableAttributes();
}
}
private void search(String searchQuery, String... expectedGroupIds) {
GroupsResource search = testRealmResource().groups();
List<String> found = search.query(searchQuery).stream()
.map(GroupRepresentation::getName)
.collect(Collectors.toList());
assertThat(found, hasSize(expectedGroupIds.length));
assertThat(found, containsInAnyOrder(expectedGroupIds));
}