Skip to content

Commit bf0bb78

Browse files
authored
Allow searching for users with all roles in a tenant (#230)
1 parent 57b056a commit bf0bb78

File tree

5 files changed

+59
-85
lines changed

5 files changed

+59
-85
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.descope.model.user.request;
2+
3+
import java.util.List;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@Builder
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
public class RolesList {
14+
List<String> values;
15+
Boolean and;
16+
}

src/main/java/com/descope/model/user/request/UserSearchRequest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ public class UserSearchRequest {
4343
/** Retrieve only users updated before the given time. */
4444
@JsonSerialize(using = InstantToMillisSerializer.class)
4545
Instant toModifiedTime;
46-
Map<String, List<String>> tenantRoleIds;
47-
Map<String, List<String>> tenantRoleNames;
46+
Map<String, RolesList> tenantRoleIds;
47+
Map<String, RolesList> tenantRoleNames;
4848
}

src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@
6262
import com.descope.proxy.ApiProxy;
6363
import com.descope.sdk.mgmt.UserService;
6464
import com.fasterxml.jackson.core.type.TypeReference;
65-
import com.fasterxml.jackson.databind.ObjectMapper;
6665
import java.net.URI;
67-
import java.util.HashMap;
6866
import java.util.List;
6967
import java.util.Map;
7068
import org.apache.commons.collections4.CollectionUtils;
@@ -256,19 +254,10 @@ public AllUsersResponseDetails searchAll(UserSearchRequest request) throws Desco
256254
if (request.getPage() == null || request.getPage() < 0) {
257255
throw ServerCommonException.invalidArgument("page");
258256
}
259-
260-
Map<String, Object> payload = new ObjectMapper().convertValue(request, new TypeReference<Map<String, Object>>() {});
261257

262-
if (request.getTenantRoleIds() != null) {
263-
payload.put("tenantRoleIds", mapToValuesObject(request.getTenantRoleIds()));
264-
}
265-
if (request.getTenantRoleNames() != null) {
266-
payload.put("tenantRoleNames", mapToValuesObject(request.getTenantRoleNames()));
267-
}
268-
269258
URI composeSearchAllUri = composeSearchAllUri();
270259
ApiProxy apiProxy = getApiProxy();
271-
return apiProxy.post(composeSearchAllUri, payload, AllUsersResponseDetails.class);
260+
return apiProxy.post(composeSearchAllUri, request, AllUsersResponseDetails.class);
272261
}
273262

274263
@Override
@@ -753,16 +742,4 @@ private URI composeGenerateEmbeddedLink() {
753742
return getUri(USER_CREATE_EMBEDDED_LINK);
754743
}
755744

756-
private static Map<String, Map<String, List<String>>> mapToValuesObject(Map<String, List<String>> inputMap) {
757-
if (inputMap == null) {
758-
return null;
759-
}
760-
Map<String, Map<String, List<String>>> result = new HashMap<>();
761-
for (Map.Entry<String, List<String>> entry : inputMap.entrySet()) {
762-
if (entry.getValue() != null) {
763-
result.put(entry.getKey(), mapOf("values", entry.getValue()));
764-
}
765-
}
766-
return result.isEmpty() ? null : result;
767-
}
768745
}

src/test/java/com/descope/model/user/request/UserSearchRequestTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,19 @@ public void testTenantRoleIdsAndNamesSerialization() throws JsonProcessingExcept
4444
UserSearchRequest request = UserSearchRequest.builder()
4545
.tenantIds(Collections.singletonList("tenant1"))
4646
.roles(Collections.singletonList("role1"))
47-
.tenantRoleIds(Collections.singletonMap("tenant1", Collections.singletonList("roleA")))
48-
.tenantRoleNames(Collections.singletonMap("tenant2", Collections.singletonList("roleX")))
47+
.tenantRoleIds(Collections.singletonMap("tenant1",
48+
RolesList.builder().values(Collections.singletonList("roleA")).build()))
49+
.tenantRoleNames(Collections.singletonMap("tenant2",
50+
RolesList.builder().values(Collections.singletonList("roleX")).build()))
4951
.build();
5052

5153
String json = mapper.writeValueAsString(request);
5254
String expectedJson = "{\"tenantIds\":[\"tenant1\"],\"roles\":[\"role1\"],\"limit\":0,\"page\":0,"
5355
+ "\"withTestUser\":null,\"testUsersOnly\":null,\"customAttributes\":null,\"statuses\":null,\"emails\":null,"
5456
+ "\"phones\":null,\"loginIds\":null,\"userIds\":null,\"ssoAppIds\":null,\"fromCreatedTime\":null,"
5557
+ "\"toCreatedTime\":null,\"fromModifiedTime\":null,\"toModifiedTime\":null,"
56-
+ "\"tenantRoleIds\":{\"tenant1\":[\"roleA\"]},"
57-
+ "\"tenantRoleNames\":{\"tenant2\":[\"roleX\"]}}";
58+
+ "\"tenantRoleIds\":{\"tenant1\":{\"values\":[\"roleA\"],\"and\":null}},"
59+
+ "\"tenantRoleNames\":{\"tenant2\":{\"values\":[\"roleX\"],\"and\":null}}}";
5860

5961
assertEquals(expectedJson, json);
6062
}

src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static com.descope.utils.CollectionUtils.mapOf;
44
import static org.assertj.core.api.Assertions.assertThat;
55
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
67
import static org.junit.jupiter.api.Assertions.assertNotNull;
78
import static org.junit.jupiter.api.Assertions.assertThrows;
89
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -30,6 +31,7 @@
3031
import com.descope.model.user.request.BatchUserPasswordPbkdf2;
3132
import com.descope.model.user.request.BatchUserRequest;
3233
import com.descope.model.user.request.PatchUserRequest;
34+
import com.descope.model.user.request.RolesList;
3335
import com.descope.model.user.request.UserRequest;
3436
import com.descope.model.user.request.UserSearchRequest;
3537
import com.descope.model.user.response.AllUsersResponseDetails;
@@ -66,7 +68,6 @@
6668
import org.junit.jupiter.api.BeforeEach;
6769
import org.junit.jupiter.api.Test;
6870
import org.junitpioneer.jupiter.RetryingTest;
69-
import org.mockito.ArgumentCaptor;
7071
import org.mockito.MockedStatic;
7172

7273
public class UserServiceImplTest {
@@ -825,50 +826,6 @@ void testSearchAllForSuccess() {
825826
}
826827
}
827828

828-
@Test
829-
void testSearchAllWithTenantRoleParams() {
830-
Map<String, List<String>> tenantRoleIds = mapOf("tenant1", Arrays.asList("roleA", "roleB"));
831-
Map<String, List<String>> tenantRoleNames = mapOf("tenant1", Arrays.asList("roleA", "roleB"));
832-
AllUsersResponseDetails allUsersResponse = mock(AllUsersResponseDetails.class);
833-
UserSearchRequest userSearchRequest = UserSearchRequest.builder()
834-
.limit(5)
835-
.page(0)
836-
.tenantRoleIds(tenantRoleIds)
837-
.tenantRoleNames(tenantRoleNames)
838-
.build();
839-
840-
ApiProxy apiProxy = mock(ApiProxy.class);
841-
doReturn(allUsersResponse).when(apiProxy).post(any(), any(), any());
842-
843-
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
844-
mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
845-
846-
userService.searchAll(userSearchRequest);
847-
848-
@SuppressWarnings("unchecked")
849-
ArgumentCaptor<Map<String, Object>> captor = ArgumentCaptor.forClass(Map.class);
850-
verify(apiProxy).post(any(), captor.capture(), any());
851-
852-
Map<String, Object> payload = captor.getValue();
853-
854-
@SuppressWarnings("unchecked")
855-
Map<String, Object> wrappedIds = (Map<String, Object>) payload.get("tenantRoleIds");
856-
assertTrue(wrappedIds.get("tenant1") instanceof Map);
857-
858-
@SuppressWarnings("unchecked")
859-
Map<String, Object> tenantIdsMap = (Map<String, Object>) wrappedIds.get("tenant1");
860-
assertEquals(Arrays.asList("roleA", "roleB"), (tenantIdsMap.get("values")));
861-
862-
@SuppressWarnings("unchecked")
863-
Map<String, Object> wrappedNames = (Map<String, Object>) payload.get("tenantRoleNames");
864-
assertTrue(wrappedNames.get("tenant1") instanceof Map);
865-
866-
@SuppressWarnings("unchecked")
867-
Map<String, Object> tenantNamesMap = (Map<String, Object>) wrappedNames.get("tenant1");
868-
assertEquals(Arrays.asList("roleA", "roleB"), (tenantNamesMap.get("values")));
869-
}
870-
}
871-
872829
@Test
873830
void testSearchAllForInvalidLimit() {
874831
ServerCommonException thrown = assertThrows(ServerCommonException.class,
@@ -1019,8 +976,10 @@ void testFunctionalUserWithTenantAndRole() {
1019976
String tenantName = TestUtils.getRandomName("t-");
1020977
String tenantId = tenantService.create(tenantName, Arrays.asList(tenantName + ".com"));
1021978
assertThat(tenantId).isNotBlank();
1022-
String roleName = TestUtils.getRandomName("r-").substring(0, 20);
1023-
roleService.create(roleName, "", null);
979+
String roleName1 = TestUtils.getRandomName("r-").substring(0, 20);
980+
roleService.create(roleName1, "", null);
981+
String roleName2 = TestUtils.getRandomName("r-").substring(0, 20);
982+
roleService.create(roleName2, "", null);
1024983
String loginId = TestUtils.getRandomName("u-");
1025984
String email = TestUtils.getRandomName("test-") + "@descope.com";
1026985
String phone = "+1-555-555-5555";
@@ -1029,7 +988,8 @@ void testFunctionalUserWithTenantAndRole() {
1029988
UserRequest.builder().email(email).verifiedEmail(true).phone(phone).verifiedPhone(true)
1030989
.displayName("Testing Test")
1031990
.userTenants(
1032-
Arrays.asList(AssociatedTenant.builder().tenantId(tenantId).roleNames(Arrays.asList(roleName)).build()))
991+
Arrays.asList(
992+
AssociatedTenant.builder().tenantId(tenantId).roleNames(Arrays.asList(roleName1, roleName2)).build()))
1033993
.build());
1034994
UserResponse user = createResponse.getUser();
1035995
assertNotNull(user);
@@ -1041,34 +1001,53 @@ void testFunctionalUserWithTenantAndRole() {
10411001
assertEquals("Testing Test", user.getName());
10421002
assertEquals("invited", user.getStatus());
10431003
assertThat(user.getUserTenants()).containsExactly(AssociatedTenant.builder().tenantId(tenantId)
1044-
.tenantName(tenantName).roleNames(Arrays.asList(roleName)).build());
1004+
.tenantName(tenantName).roleNames(Arrays.asList(roleName1, roleName2)).build());
10451005
UserResponseDetails updateResponse = userService.update(loginId,
1046-
UserRequest.builder().roleNames(Arrays.asList(roleName)).email(email).verifiedEmail(true).phone(phone)
1006+
UserRequest.builder().roleNames(
1007+
Arrays.asList(roleName1, roleName2)).email(email).verifiedEmail(true).phone(phone)
10471008
.verifiedPhone(true).displayName("Testing Test").build());
10481009
user = updateResponse.getUser();
10491010
assertNotNull(user);
1050-
assertThat(user.getRoleNames()).containsExactly(roleName);
1011+
assertThat(user.getRoleNames()).containsExactly(roleName1, roleName2);
10511012
// Patch
10521013
UserResponseDetails patchResponse = userService.patch(loginId,
10531014
PatchUserRequest.builder()
10541015
.userTenants(
10551016
Arrays.asList(AssociatedTenant.builder()
1056-
.tenantId(tenantId).roleNames(Arrays.asList(roleName)).build())).build());
1017+
.tenantId(tenantId).roleNames(Arrays.asList(roleName1, roleName2)).build())).build());
10571018
user = patchResponse.getUser();
10581019
assertNotNull(user);
10591020
assertThat(user.getUserTenants()).containsExactly(AssociatedTenant.builder().tenantId(tenantId)
1060-
.tenantName(tenantName).roleNames(Arrays.asList(roleName)).build());
1021+
.tenantName(tenantName).roleNames(Arrays.asList(roleName1, roleName2)).build());
10611022
// Search
10621023
AllUsersResponseDetails searchByTenantRoleNames = userService.searchAll(
10631024
UserSearchRequest.builder()
1064-
.tenantRoleNames(mapOf(tenantId, Arrays.asList(roleName))).build());
1025+
.tenantRoleNames(
1026+
mapOf(tenantId, RolesList.builder().values(
1027+
Arrays.asList(roleName1, roleName2)).and(true).build())).build());
10651028
boolean foundByRoleName = searchByTenantRoleNames.getUsers().stream()
10661029
.anyMatch(u -> u.getUserId().equals(createResponse.getUser().getUserId()));
10671030
assertTrue(foundByRoleName);
1031+
patchResponse = userService.patch(loginId,
1032+
PatchUserRequest.builder()
1033+
.userTenants(
1034+
Arrays.asList(AssociatedTenant.builder()
1035+
.tenantId(tenantId).roleNames(Arrays.asList(roleName1)).build())).build());
1036+
user = patchResponse.getUser();
1037+
assertNotNull(user);
1038+
searchByTenantRoleNames = userService.searchAll(
1039+
UserSearchRequest.builder()
1040+
.tenantRoleNames(
1041+
mapOf(tenantId, RolesList.builder().values(
1042+
Arrays.asList(roleName1, roleName2)).and(true).build())).build());
1043+
foundByRoleName = searchByTenantRoleNames.getUsers().stream()
1044+
.anyMatch(u -> u.getUserId().equals(createResponse.getUser().getUserId()));
1045+
assertFalse(foundByRoleName);
10681046
// Delete
10691047
userService.delete(loginId);
10701048
tenantService.delete(tenantId);
1071-
roleService.delete(roleName);
1049+
roleService.delete(roleName1);
1050+
roleService.delete(roleName2);
10721051
}
10731052

10741053
@RetryingTest(value = 3, suspendForMs = 30000, onExceptions = RateLimitExceededException.class)

0 commit comments

Comments
 (0)