diff --git a/README.md b/README.md index 0c0352e0..9e1651d4 100644 --- a/README.md +++ b/README.md @@ -713,7 +713,7 @@ try { // Handle the error } -// Update will override all fields as is. Use carefully. +// Update will override all fields as is. Use carefully. Use patch instead if providing select fields. try { us.update("desmond@descope.com", UserRequest.builder() .email("desmond@descope.com") @@ -723,7 +723,17 @@ try { .tenantId("tenant-ID1") .roleNames(Arrays.asList("role-name1"), AssociatedTenant.builder() - .tenantId("tenant-ID2"))))); + .tenantId("tenant-ID2")))) + .build()); +} catch (DescopeException de) { + // Handle the error +} + +// Patch will override provided fields but will leave other fields untouched. +try { + us.patch("desmond@descope.com", PatchUserRequest.builder() + .name("Desmond Copeland") + .build()); } catch (DescopeException de) { // Handle the error } diff --git a/examples/management-cli/pom.xml b/examples/management-cli/pom.xml index 88419f88..d4579d46 100644 --- a/examples/management-cli/pom.xml +++ b/examples/management-cli/pom.xml @@ -19,7 +19,7 @@ com.descope java-sdk - 1.0.26 + 1.0.27 info.picocli diff --git a/pom.xml b/pom.xml index 469f8a1a..bc323fd2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.descope java-sdk 4.0.0 - 1.0.26 + 1.0.27 ${project.groupId}:${project.artifactId} Java library used to integrate with Descope. https://github.com/descope/descope-java diff --git a/src/main/java/com/descope/literals/Routes.java b/src/main/java/com/descope/literals/Routes.java index ce67d082..05176817 100644 --- a/src/main/java/com/descope/literals/Routes.java +++ b/src/main/java/com/descope/literals/Routes.java @@ -85,6 +85,7 @@ public static class ManagementEndPoints { // User public static final String CREATE_USER_LINK = "/v1/mgmt/user/create"; public static final String CREATE_USERS_BATCH_LINK = "/v1/mgmt/user/create/batch"; + public static final String PATCH_USER_LINK = "/v1/mgmt/user/patch"; public static final String UPDATE_USER_LINK = "/v1/mgmt/user/update"; public static final String DELETE_USER_LINK = "/v1/mgmt/user/delete"; public static final String DELETE_ALL_TEST_USERS_LINK = "/v1/mgmt/user/test/delete/all"; @@ -125,7 +126,7 @@ public static class ManagementEndPoints { public static final String LOAD_ALL_TENANTS_LINK = "/v1/mgmt/tenant/all"; public static final String TENANT_SEARCH_ALL_LINK = "/v1/mgmt/tenant/search"; public static final String GET_TENANT_SETTINGS_LINK = "/v1/mgmt/tenant/settings"; - + // SSO public static final String SSO_GET_SETTINGS_LINK = "/v1/mgmt/sso/settings"; public static final String SSO_DELETE_SETTINGS_LINK = "/v1/mgmt/sso/settings"; diff --git a/src/main/java/com/descope/model/user/request/PatchUserRequest.java b/src/main/java/com/descope/model/user/request/PatchUserRequest.java new file mode 100644 index 00000000..00fe9490 --- /dev/null +++ b/src/main/java/com/descope/model/user/request/PatchUserRequest.java @@ -0,0 +1,49 @@ +package com.descope.model.user.request; + +import static com.descope.utils.CollectionUtils.addIfNotNull; + +import com.descope.model.auth.AssociatedTenant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class PatchUserRequest { + String name; + String givenName; + String middleName; + String familyName; + String phone; + String email; + List roleNames; + List userTenants; + Map customAttributes; + String picture; + Boolean verifiedEmail; + Boolean verifiedPhone; + List ssoAppIds; + + public Map toMap() { + Map m = new HashMap<>(); + addIfNotNull(m, "email", email); + addIfNotNull(m, "verifiedEmail", verifiedEmail); + addIfNotNull(m, "phone", phone); + addIfNotNull(m, "verifiedPhone", verifiedPhone); + addIfNotNull(m, "givenName", givenName); + addIfNotNull(m, "middleName", middleName); + addIfNotNull(m, "familyName", familyName); + addIfNotNull(m, "roleNames", roleNames); + addIfNotNull(m, "userTenants", userTenants); + addIfNotNull(m, "customAttributes", customAttributes); + addIfNotNull(m, "picture", picture); + addIfNotNull(m, "ssoAppIDs", ssoAppIds); + return m; + } +} \ No newline at end of file diff --git a/src/main/java/com/descope/model/user/request/UserRequest.java b/src/main/java/com/descope/model/user/request/UserRequest.java index 18f4ecf3..2de39c36 100644 --- a/src/main/java/com/descope/model/user/request/UserRequest.java +++ b/src/main/java/com/descope/model/user/request/UserRequest.java @@ -1,6 +1,5 @@ package com.descope.model.user.request; - import static com.descope.utils.CollectionUtils.addIfNotNull; import com.descope.model.auth.AssociatedTenant; diff --git a/src/main/java/com/descope/proxy/ApiProxy.java b/src/main/java/com/descope/proxy/ApiProxy.java index 81804e69..9f783ca6 100644 --- a/src/main/java/com/descope/proxy/ApiProxy.java +++ b/src/main/java/com/descope/proxy/ApiProxy.java @@ -10,6 +10,8 @@ public interface ApiProxy { R post(URI uri, B body, Class returnClz); + R patch(URI uri, B body, Class returnClz); + R postAndGetArray(URI uri, B body, TypeReference typeReference); R delete(URI uri, B body, Class returnClz); diff --git a/src/main/java/com/descope/proxy/impl/AbstractProxyImpl.java b/src/main/java/com/descope/proxy/impl/AbstractProxyImpl.java index 278d442d..fd6dd6ae 100644 --- a/src/main/java/com/descope/proxy/impl/AbstractProxyImpl.java +++ b/src/main/java/com/descope/proxy/impl/AbstractProxyImpl.java @@ -75,7 +75,7 @@ public R handleResponse(ClassicHttpResponse response) throws HttpException, IOEx .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); final ByteArrayOutputStream bs = new ByteArrayOutputStream(); final TeeInputStream tee = new TeeInputStream(res.getEntity().getContent(), bs, true); - + if (res.getCode() < 200 || response.getCode() > 299) { if (res.getCode() == 429) { // Rate limit from infra throw new RateLimitExceededException( @@ -118,7 +118,7 @@ public R handleResponse(ClassicHttpResponse response) throws HttpException, IOEx throw ServerCommonException.parseResponseError("Error parsing response", bs.toString(), e); } } - } + } }); } } @@ -167,6 +167,17 @@ protected R post(URI uri, B body, TypeReference typeReference) { return exchange(builder.build(), null, typeReference); } + @SneakyThrows + protected R patch(URI uri, B body, Class returnClz) { + final ClassicRequestBuilder builder = ClassicRequestBuilder.patch(uri); + if (body != null) { + final ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(Include.NON_NULL); + final byte[] payload = objectMapper.writeValueAsBytes(body); + builder.setEntity(new ByteArrayEntity(payload, ContentType.APPLICATION_JSON)); + } + return exchange(builder.build(), returnClz, null); + } + protected R get(URI uri, Class returnClz) { return exchange(ClassicRequestBuilder.get(uri).build(), returnClz, null); } diff --git a/src/main/java/com/descope/proxy/impl/ApiProxyImpl.java b/src/main/java/com/descope/proxy/impl/ApiProxyImpl.java index c5b95c0d..a476434a 100644 --- a/src/main/java/com/descope/proxy/impl/ApiProxyImpl.java +++ b/src/main/java/com/descope/proxy/impl/ApiProxyImpl.java @@ -22,6 +22,11 @@ public R post(URI uri, B body, Class returnClz) { return super.post(uri, body, returnClz); } + @Override + public R patch(URI uri, B body, Class returnClz) { + return super.patch(uri, body, returnClz); + } + @Override public R postAndGetArray(URI uri, B body, TypeReference typeReference) { return super.post(uri, body, typeReference); diff --git a/src/main/java/com/descope/sdk/mgmt/UserService.java b/src/main/java/com/descope/sdk/mgmt/UserService.java index cf66c826..4e410f06 100644 --- a/src/main/java/com/descope/sdk/mgmt/UserService.java +++ b/src/main/java/com/descope/sdk/mgmt/UserService.java @@ -4,6 +4,7 @@ import com.descope.exception.DescopeException; import com.descope.model.auth.InviteOptions; import com.descope.model.user.request.BatchUserRequest; +import com.descope.model.user.request.PatchUserRequest; import com.descope.model.user.request.UserRequest; import com.descope.model.user.request.UserSearchRequest; import com.descope.model.user.response.AllUsersResponseDetails; @@ -90,6 +91,7 @@ public interface UserService { * *

IMPORTANT: All parameters will override whatever values are currently set in the existing * user. Use carefully. + * Instead, use Patch if you don't want to pass all parameters. * * @param loginId The loginID is required and will determine what the user will use to sign in, * make sure the login id is unique for test. @@ -100,6 +102,18 @@ public interface UserService { */ UserResponseDetails update(String loginId, UserRequest request) throws DescopeException; + /** + * Patches an existing user. + * + *

Only the fields that are set in the request will be updated. + * + * @param loginId The loginID is required and will determine which user to update. + * @param request The request containing the fields to be updated. Fields not set will remain unchanged. + * @return {@link UserResponseDetails UserResponseDetails} containing the updated user details. + * @throws DescopeException If there occurs any exception, a subtype of this exception will be thrown. + */ + UserResponseDetails patch(String loginId, PatchUserRequest request) throws DescopeException; + /** * Logout user from all devices. * diff --git a/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java b/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java index 77f1a562..14dfd0dc 100644 --- a/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java +++ b/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java @@ -10,6 +10,7 @@ import static com.descope.literals.Routes.ManagementEndPoints.LOAD_USER_LINK; import static com.descope.literals.Routes.ManagementEndPoints.LOGOUT_USER_LINK; import static com.descope.literals.Routes.ManagementEndPoints.MAGIC_LINK_FOR_TEST_LINK; +import static com.descope.literals.Routes.ManagementEndPoints.PATCH_USER_LINK; import static com.descope.literals.Routes.ManagementEndPoints.UPDATE_CUSTOM_ATTRIBUTE_LINK; import static com.descope.literals.Routes.ManagementEndPoints.UPDATE_PICTURE_LINK; import static com.descope.literals.Routes.ManagementEndPoints.UPDATE_USER_LINK; @@ -46,6 +47,7 @@ import com.descope.model.user.request.GenerateEmbeddedLinkRequest; import com.descope.model.user.request.MagicLinkTestUserRequest; import com.descope.model.user.request.OTPTestUserRequest; +import com.descope.model.user.request.PatchUserRequest; import com.descope.model.user.request.UserRequest; import com.descope.model.user.request.UserSearchRequest; import com.descope.model.user.response.AllUsersResponseDetails; @@ -151,6 +153,21 @@ public UsersBatchResponse inviteBatch(List users, InviteOption return apiProxy.post(createUsersUri, req, UsersBatchResponse.class); } + @Override + public UserResponseDetails patch(String loginId, PatchUserRequest request) throws DescopeException { + if (StringUtils.isBlank(loginId)) { + throw ServerCommonException.invalidArgument("Login ID"); + } + if (request == null) { + request = new PatchUserRequest(); + } + Map req = mapOf("loginId", loginId); + req.putAll(request.toMap()); + URI patchUserUri = composePatchUserUri(); + ApiProxy apiProxy = getApiProxy(); + return apiProxy.patch(patchUserUri, req, UserResponseDetails.class); + } + @Override public UserResponseDetails update(String loginId, UserRequest request) throws DescopeException { if (StringUtils.isBlank(loginId)) { @@ -583,7 +600,7 @@ public List history(List userIds) throws DescopeExc } ApiProxy apiProxy = getApiProxy(); return apiProxy.postAndGetArray(getUri(USER_HISTORY_LINK), userIds, - new TypeReference>() {}); + new TypeReference>() {}); } public String generateEmbeddedLink( @@ -594,8 +611,8 @@ public String generateEmbeddedLink( URI generateEmbeddedLinkUri = composeGenerateEmbeddedLink(); GenerateEmbeddedLinkRequest request = new GenerateEmbeddedLinkRequest(loginId, customClaims); ApiProxy apiProxy = getApiProxy(); - GenerateEmbeddedLinkResponse response = - apiProxy.post(generateEmbeddedLinkUri, request, GenerateEmbeddedLinkResponse.class); + GenerateEmbeddedLinkResponse response = apiProxy.post(generateEmbeddedLinkUri, request, + GenerateEmbeddedLinkResponse.class); return response.getToken(); } @@ -607,6 +624,10 @@ private URI composeCreateBatchUsersUri() { return getUri(CREATE_USERS_BATCH_LINK); } + private URI composePatchUserUri() { + return getUri(PATCH_USER_LINK); + } + private URI composeUpdateUserUri() { return getUri(UPDATE_USER_LINK); } diff --git a/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java b/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java index 8eaf7828..8d7988e4 100644 --- a/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java +++ b/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java @@ -8,11 +8,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.descope.enums.BatchUserPasswordAlgorithm; import com.descope.enums.DeliveryMethod; @@ -27,6 +29,7 @@ import com.descope.model.mgmt.ManagementServices; import com.descope.model.user.request.BatchUserPasswordHashed; import com.descope.model.user.request.BatchUserRequest; +import com.descope.model.user.request.PatchUserRequest; import com.descope.model.user.request.UserRequest; import com.descope.model.user.request.UserSearchRequest; import com.descope.model.user.response.AllUsersResponseDetails; @@ -162,6 +165,61 @@ void testUpdateForSuccess() { } } + @Test + void testPatchForEmptyLoginId() { + ServerCommonException thrown = assertThrows(ServerCommonException.class, + () -> userService.patch("", new PatchUserRequest())); + assertNotNull(thrown); + assertEquals("The Login ID argument is invalid", thrown.getMessage()); + } + + @Test + void testUserPatchError() { + UserService userService = mock(UserService.class); + PatchUserRequest user = new PatchUserRequest(); + String email = "foo@bar.com"; + user.setEmail(email); + + when(userService.patch(anyString(), any(PatchUserRequest.class))).thenThrow(new RuntimeException("Error")); + + assertThrows(RuntimeException.class, () -> userService.patch("123", user)); + } + + @Test + void testUserPatchSuccess() { + PatchUserRequest user = new PatchUserRequest(); + user.setName("name1"); + user.setMiddleName("middleName1"); + user.setPhone("+9724567890"); + user.setVerifiedPhone(true); + user.setPicture("https://test.com"); + user.setRoleNames(Arrays.asList("foo", "bar")); + + UserResponseDetails expectedResponse = new UserResponseDetails(); + expectedResponse.setUser(new UserResponse()); + expectedResponse.getUser().setName("name1"); + expectedResponse.getUser().setMiddleName("middleName1"); + expectedResponse.getUser().setPhone("+9724567890"); + expectedResponse.getUser().setVerifiedPhone(true); + expectedResponse.getUser().setPicture("https://test.com"); + expectedResponse.getUser().setRoleNames(Arrays.asList("foo", "bar")); + + ApiProxy apiProxy = mock(ApiProxy.class); + doReturn(expectedResponse).when(apiProxy).patch(any(), any(), any()); + try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { + mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); + UserResponseDetails response = userService.patch("someLoginId", user); + Assertions.assertThat(response).isNotNull(); + assertNotNull(response.getUser()); + assertEquals(expectedResponse.getUser().getName(), response.getUser().getName()); + assertEquals(expectedResponse.getUser().getMiddleName(), response.getUser().getMiddleName()); + assertEquals(expectedResponse.getUser().getPhone(), response.getUser().getPhone()); + assertTrue(response.getUser().getVerifiedPhone()); + assertEquals(expectedResponse.getUser().getPicture(), response.getUser().getPicture()); + assertEquals(expectedResponse.getUser().getRoleNames(), response.getUser().getRoleNames()); + } + } + @Test void testLogoutForEmptyLoginId() { ServerCommonException thrown = assertThrows(ServerCommonException.class, () -> userService.logoutUser("")); @@ -704,8 +762,8 @@ void testGenerateMagicLinkForTestUserForSuccess() { doReturn(mockResponse).when(apiProxy).post(any(), any(), any()); try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); - MagicLinkTestUserResponse response = - userService.generateMagicLinkForTestUser("someLoginId", mockUrl, DeliveryMethod.EMAIL); + MagicLinkTestUserResponse response = userService.generateMagicLinkForTestUser("someLoginId", mockUrl, + DeliveryMethod.EMAIL); Assertions.assertThat(response.getLink()).isEqualTo("link"); } } @@ -787,9 +845,9 @@ void testFunctionalFullCycle() { String phone = "+1-555-555-5555"; List additionalLoginIds = Arrays.asList(TestUtils.getRandomName("u-"), TestUtils.getRandomName("u-")); // Create - UserResponseDetails createResponse = userService.create(loginId, UserRequest.builder().email(email) - .verifiedEmail(true).phone(phone).verifiedPhone(true).displayName("Testing Test") - .additionalLoginIds(additionalLoginIds).build()); + UserResponseDetails createResponse = userService.create(loginId, + UserRequest.builder().email(email).verifiedEmail(true).phone(phone).verifiedPhone(true) + .displayName("Testing Test").additionalLoginIds(additionalLoginIds).build()); UserResponse user = createResponse.getUser(); assertNotNull(user); Assertions.assertThat(user.getLoginIds()).contains(loginId); @@ -854,6 +912,12 @@ void testFunctionalFullCycle() { } assertTrue(found); assertTrue(searchResponse.getTotal() > 0); + PatchUserRequest pur = new PatchUserRequest(); + pur.setFamilyName("hurray patch works"); + userService.patch(newLoginId, pur); + + loadResponse = userService.loadByUserId(createResponse.getUser().getUserId()); + assertEquals(loadResponse.getUser().getFamilyName(), pur.getFamilyName()); // Delete userService.delete(newLoginId); } @@ -864,9 +928,8 @@ void testFunctionalTestUsers() { String email = TestUtils.getRandomName("test-") + "@descope.com"; String phone = "+1-555-555-5555"; // Create - UserResponseDetails createResponse = userService.createTestUser(loginId, UserRequest.builder() - .email(email).verifiedEmail(true).phone(phone).verifiedPhone(true) - .displayName("Testing Test").build()); + UserResponseDetails createResponse = userService.createTestUser(loginId, UserRequest.builder().email(email) + .verifiedEmail(true).phone(phone).verifiedPhone(true).displayName("Testing Test").build()); UserResponse user = createResponse.getUser(); assertNotNull(user); Assertions.assertThat(user.getLoginIds()).contains(loginId); @@ -918,7 +981,7 @@ void testFunctionalUserWithTenantAndRole() { UserRequest.builder().email(email).verifiedEmail(true).phone(phone).verifiedPhone(true) .displayName("Testing Test") .userTenants( - Arrays.asList(AssociatedTenant.builder().tenantId(tenantId).roleNames(Arrays.asList(roleName)).build())) + Arrays.asList(AssociatedTenant.builder().tenantId(tenantId).roleNames(Arrays.asList(roleName)).build())) .build()); UserResponse user = createResponse.getUser(); assertNotNull(user); @@ -929,12 +992,11 @@ void testFunctionalUserWithTenantAndRole() { assertEquals(true, user.getVerifiedPhone()); assertEquals("Testing Test", user.getName()); assertEquals("invited", user.getStatus()); - assertThat(user.getUserTenants()).containsExactly( - AssociatedTenant.builder().tenantId(tenantId).tenantName(tenantName).roleNames( - Arrays.asList(roleName)).build()); + assertThat(user.getUserTenants()).containsExactly(AssociatedTenant.builder().tenantId(tenantId) + .tenantName(tenantName).roleNames(Arrays.asList(roleName)).build()); UserResponseDetails updateResponse = userService.update(loginId, - UserRequest.builder().roleNames(Arrays.asList(roleName)).email(email).verifiedEmail(true) - .phone(phone).verifiedPhone(true).displayName("Testing Test").build()); + UserRequest.builder().roleNames(Arrays.asList(roleName)).email(email).verifiedEmail(true).phone(phone) + .verifiedPhone(true).displayName("Testing Test").build()); user = updateResponse.getUser(); assertNotNull(user); assertThat(user.getRoleNames()).containsExactly(roleName); @@ -1003,8 +1065,8 @@ void testFunctionalGenerateEmbeddedLinkWithPhoneAsID() { String phone = "+1-555-555-" + randomSaffix; String cleanPhone = "+1555555" + randomSaffix; // Create - UserResponseDetails createResponse = userService.create(phone, UserRequest.builder().phone(phone) - .verifiedPhone(true).displayName("Testing Test").build()); + UserResponseDetails createResponse = userService.create(phone, + UserRequest.builder().phone(phone).verifiedPhone(true).displayName("Testing Test").build()); UserResponse user = createResponse.getUser(); assertNotNull(user); Assertions.assertThat(user.getLoginIds()).contains(cleanPhone); @@ -1033,19 +1095,11 @@ void testFunctionalBatch() throws Exception { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] hash = factory.generateSecret(spec).getEncoded(); - UsersBatchResponse res = userService.createBatch(Arrays.asList(BatchUserRequest.builder() - .loginId(loginId) - .email(email) - .verifiedEmail(true) - .phone(phone) - .verifiedPhone(true) - .displayName(name) + UsersBatchResponse res = userService.createBatch(Arrays.asList(BatchUserRequest.builder().loginId(loginId) + .email(email).verifiedEmail(true).phone(phone).verifiedPhone(true).displayName(name) .hashedPassword(BatchUserPasswordHashed.builder() - .algorithm(BatchUserPasswordAlgorithm.BATCH_USER_PASSWORD_ALGORITHM_PBKDF2SHA1) - .hash(hash) - .salt(salt) - .iterations(65536) - .build()) + .algorithm(BatchUserPasswordAlgorithm.BATCH_USER_PASSWORD_ALGORITHM_PBKDF2SHA1).hash(hash).salt(salt) + .iterations(65536).build()) .build())); assertNotNull(res); assertNotNull(res.getCreatedUsers()); @@ -1065,13 +1119,8 @@ void testFunctionalSetActivePassword() { String email = TestUtils.getRandomName("test-") + "@descope.com"; String phone = "+1-555-555-5555"; // Create - UserResponseDetails createResponse = userService.create(loginId, UserRequest.builder() - .email(email) - .verifiedEmail(true) - .phone(phone) - .verifiedPhone(true) - .displayName("Testing Test") - .build()); + UserResponseDetails createResponse = userService.create(loginId, UserRequest.builder().email(email) + .verifiedEmail(true).phone(phone).verifiedPhone(true).displayName("Testing Test").build()); UserResponse user = createResponse.getUser(); assertNotNull(user); Assertions.assertThat(user.getLoginIds()).contains(loginId);