Extend group search attribute functionality to account for use case where only the leaf group is required
This commit is contained in:
parent
9bc30f4705
commit
7e56938b74
4 changed files with 42 additions and 8 deletions
|
@ -146,4 +146,8 @@ public interface GroupsResource {
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<GroupRepresentation> query(@QueryParam("q") String searchQuery);
|
List<GroupRepresentation> query(@QueryParam("q") String searchQuery);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
List<GroupRepresentation> query(@QueryParam("q") String searchQuery, @QueryParam("populateHierarchy") boolean populateHierarchy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,8 +150,10 @@ public class ModelToRepresentation {
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<GroupRepresentation> searchGroupsByAttributes(KeycloakSession session, RealmModel realm, boolean full, Map<String,String> attributes, Integer first, Integer max) {
|
public static Stream<GroupRepresentation> searchGroupsByAttributes(KeycloakSession session, RealmModel realm, boolean full, boolean populateHierarchy, Map<String,String> attributes, Integer first, Integer max) {
|
||||||
return session.groups().searchGroupsByAttributes(realm, attributes, first, 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,
|
// 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
|
// therefore for each group from the result, we need to find root group
|
||||||
.map(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
|
// More child groups of one root can fulfill the search, so we need to filter duplicates
|
||||||
.filter(StreamsUtil.distinctByKey(GroupModel::getId))
|
.filter(StreamsUtil.distinctByKey(GroupModel::getId));
|
||||||
|
}
|
||||||
// and then turn the result into GroupRepresentations creating whole hierarchy of child groups for each root group
|
// and then turn the result into GroupRepresentations creating whole hierarchy of child groups for each root group
|
||||||
.map(g -> toGroupHierarchy(g, full, attributes));
|
return groups.map(g -> toGroupHierarchy(g, full, attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
|
|
@ -79,12 +79,13 @@ public class GroupsResource {
|
||||||
@QueryParam("exact") @DefaultValue("false") Boolean exact,
|
@QueryParam("exact") @DefaultValue("false") Boolean exact,
|
||||||
@QueryParam("first") Integer firstResult,
|
@QueryParam("first") Integer firstResult,
|
||||||
@QueryParam("max") Integer maxResults,
|
@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();
|
auth.groups().requireList();
|
||||||
|
|
||||||
if (Objects.nonNull(searchQuery)) {
|
if (Objects.nonNull(searchQuery)) {
|
||||||
Map<String, String> attributes = SearchQueryUtils.getFields(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)) {
|
} else if (Objects.nonNull(search)) {
|
||||||
return ModelToRepresentation.searchForGroupByName(session, realm, !briefRepresentation, search.trim(), exact, firstResult, maxResults);
|
return ModelToRepresentation.searchForGroupByName(session, realm, !briefRepresentation, search.trim(), exact, firstResult, maxResults);
|
||||||
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
|
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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) {
|
private void search(String searchQuery, String... expectedGroupIds) {
|
||||||
GroupsResource search = testRealmResource().groups();
|
GroupsResource search = testRealmResource().groups();
|
||||||
List<String> found = search.query(searchQuery).stream()
|
List<String> found = search.query(searchQuery).stream()
|
||||||
.map(GroupRepresentation::getName)
|
.map(GroupRepresentation::getName)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
assertThat(found, hasSize(expectedGroupIds.length));
|
||||||
assertThat(found, containsInAnyOrder(expectedGroupIds));
|
assertThat(found, containsInAnyOrder(expectedGroupIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue