From 251eeb94c06eb046963fb790c784347b28246e6f Mon Sep 17 00:00:00 2001 From: Slavik Markovich Date: Thu, 13 Aug 2026 14:48:40 -0700 Subject: [PATCH 1/2] Added failOnConflict to both update UserService.updateEmail and updatePhone --- src/main/java/com/descope/sdk/mgmt/UserService.java | 12 ++++++++---- .../com/descope/sdk/mgmt/impl/UserServiceImpl.java | 12 ++++++++---- .../descope/sdk/mgmt/impl/UserServiceImplTest.java | 12 ++++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/descope/sdk/mgmt/UserService.java b/src/main/java/com/descope/sdk/mgmt/UserService.java index e096f281..c04a789f 100644 --- a/src/main/java/com/descope/sdk/mgmt/UserService.java +++ b/src/main/java/com/descope/sdk/mgmt/UserService.java @@ -229,12 +229,14 @@ public interface UserService { * @param email The email parameter can be empty in which case the email will be removed. * @param isVerified The isVerified flag must be true for the user to be able to login with the * email address. + * @param failOnConflict The failOnConflict flag indicates whether to fail the update if the new + * email is also the login ID of another user. * @return {@link UserResponseDetails UserResponseDetails} * @throws DescopeException If there occurs any exception, a subtype of this exception will be * thrown. */ - UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified) - throws DescopeException; + UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified, + Boolean failOnConflict) throws DescopeException; /** * Update the email address for an existing user. @@ -243,12 +245,14 @@ UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified * @param phone The phone parameter can be empty in which case the phone will be removed. * @param isVerified The isVerified flag must be true for the user to be able to login with the * email address. + * @param failOnConflict The failOnConflict flag indicates whether to fail the update if the new + * phone number is also the login ID of another user. * @return {@link UserResponseDetails UserResponseDetails} * @throws DescopeException If there occurs any exception, a subtype of this exception will be * thrown. */ - UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified) - throws DescopeException; + UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified, + Boolean failOnConflict) throws DescopeException; /** * Update an existing user's display name (i.e., their full name). 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 47cc8159..743ba44c 100644 --- a/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java +++ b/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java @@ -381,23 +381,27 @@ public UserResponseDetails deactivate(String loginId) throws DescopeException { } @Override - public UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified) throws DescopeException { + public UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified, + Boolean failOnConflict) throws DescopeException { if (StringUtils.isBlank(loginId)) { throw ServerCommonException.invalidArgument("Login ID"); } URI updateEmailUri = composeUpdateEmailUri(); - Map request = mapOf("loginId", loginId, "email", email, "verified", isVerified); + Map request = mapOf("loginId", loginId, "email", email, "verified", isVerified, + "failOnConflict", failOnConflict); ApiProxy apiProxy = getApiProxy(); return apiProxy.post(updateEmailUri, request, UserResponseDetails.class); } @Override - public UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified) throws DescopeException { + public UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified, + Boolean failOnConflict) throws DescopeException { if (StringUtils.isBlank(loginId)) { throw ServerCommonException.invalidArgument("Login ID"); } URI updatePhoneUri = composeUpdatePhoneUri(); - Map request = mapOf("loginId", loginId, "phone", phone, "verified", isVerified); + Map request = mapOf("loginId", loginId, "phone", phone, "verified", isVerified, + "failOnConflict", failOnConflict); ApiProxy apiProxy = getApiProxy(); return apiProxy.post(updatePhoneUri, request, UserResponseDetails.class); } 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 6d6400d0..02d0fb5c 100644 --- a/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java +++ b/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java @@ -350,7 +350,7 @@ void testDeactivateForSuccess() { @Test void testUpdateEmailForEmptyLoginId() { ServerCommonException thrown = assertThrows(ServerCommonException.class, - () -> userService.updateEmail("", "someEmail", false)); + () -> userService.updateEmail("", "someEmail", false, false)); assertNotNull(thrown); assertEquals("The Login ID argument is invalid", thrown.getMessage()); } @@ -362,7 +362,7 @@ void testUpdateEmailForSuccess() { doReturn(userResponseDetails).when(apiProxy).post(any(), any(), any()); try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); - UserResponseDetails response = userService.updateEmail("someLoginId", "someEmail", false); + UserResponseDetails response = userService.updateEmail("someLoginId", "someEmail", false, false); Assertions.assertThat(response).isNotNull(); } } @@ -370,7 +370,7 @@ void testUpdateEmailForSuccess() { @Test void testUpdatePhoneForEmptyLoginId() { ServerCommonException thrown = assertThrows(ServerCommonException.class, - () -> userService.updatePhone("", "someEmail", false)); + () -> userService.updatePhone("", "someEmail", false, false)); assertNotNull(thrown); assertEquals("The Login ID argument is invalid", thrown.getMessage()); } @@ -382,7 +382,7 @@ void testUpdatePhoneForSuccess() { doReturn(userResponseDetails).when(apiProxy).post(any(), any(), any()); try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); - UserResponseDetails response = userService.updatePhone("someLoginId", "1234567890", false); + UserResponseDetails response = userService.updatePhone("someLoginId", "1234567890", false, false); Assertions.assertThat(response).isNotNull(); } } @@ -886,8 +886,8 @@ void testFunctionalFullCycle() { userService.updateDisplayName(loginId, "Testing Test"); userService.updateDisplayNames(loginId, "G Test", "M Test", "F Test"); email = TestUtils.getRandomName("test-") + "@descope.com"; - userService.updateEmail(loginId, email, true); - userService.updatePhone(loginId, "+1-555-555-6666", true); + userService.updateEmail(loginId, email, true, false); + userService.updatePhone(loginId, "+1-555-555-6666", true, false); String newLoginId = TestUtils.getRandomName("u-"); userService.updateLoginId(loginId, newLoginId); UserResponseDetails loadResponse = userService.load(newLoginId); From cbac5a61d7b30c9d8f1ec05270aebae43d8bc9d1 Mon Sep 17 00:00:00 2001 From: Slavik Markovich Date: Thu, 13 Aug 2026 14:58:28 -0700 Subject: [PATCH 2/2] Keeping backwards compatible --- .../com/descope/sdk/mgmt/UserService.java | 30 ++++++++++++++++- .../sdk/mgmt/impl/UserServiceImpl.java | 10 ++++++ .../sdk/mgmt/impl/UserServiceImplTest.java | 32 ++++++++++++++++--- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/descope/sdk/mgmt/UserService.java b/src/main/java/com/descope/sdk/mgmt/UserService.java index c04a789f..022d5be0 100644 --- a/src/main/java/com/descope/sdk/mgmt/UserService.java +++ b/src/main/java/com/descope/sdk/mgmt/UserService.java @@ -222,6 +222,20 @@ public interface UserService { */ UserResponseDetails deactivate(String loginId) throws DescopeException; + /** + * Update the email address for an existing user. + * + * @param loginId The loginID is required. + * @param email The email parameter can be empty in which case the email will be removed. + * @param isVerified The isVerified flag must be true for the user to be able to login with the + * email address. + * @return {@link UserResponseDetails UserResponseDetails} + * @throws DescopeException If there occurs any exception, a subtype of this exception will be + * thrown. + */ + UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified) + throws DescopeException; + /** * Update the email address for an existing user. * @@ -239,7 +253,21 @@ UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified Boolean failOnConflict) throws DescopeException; /** - * Update the email address for an existing user. + * Update the phone number for an existing user. + * + * @param loginId The loginID is required. + * @param phone The phone parameter can be empty in which case the phone will be removed. + * @param isVerified The isVerified flag must be true for the user to be able to login with the + * email address. + * @return {@link UserResponseDetails UserResponseDetails} + * @throws DescopeException If there occurs any exception, a subtype of this exception will be + * thrown. + */ + UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified) + throws DescopeException; + + /** + * Update the phone number for an existing user. * * @param loginId The loginID is required. * @param phone The phone parameter can be empty in which case the phone will be removed. 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 743ba44c..9499d78c 100644 --- a/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java +++ b/src/main/java/com/descope/sdk/mgmt/impl/UserServiceImpl.java @@ -380,6 +380,11 @@ public UserResponseDetails deactivate(String loginId) throws DescopeException { return apiProxy.post(activateUserUri, request, UserResponseDetails.class); } + @Override + public UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified) throws DescopeException { + return updateEmail(loginId, email, isVerified, false); + } + @Override public UserResponseDetails updateEmail(String loginId, String email, Boolean isVerified, Boolean failOnConflict) throws DescopeException { @@ -393,6 +398,11 @@ public UserResponseDetails updateEmail(String loginId, String email, Boolean isV return apiProxy.post(updateEmailUri, request, UserResponseDetails.class); } + @Override + public UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified) throws DescopeException { + return updatePhone(loginId, phone, isVerified, false); + } + @Override public UserResponseDetails updatePhone(String loginId, String phone, Boolean isVerified, Boolean failOnConflict) throws DescopeException { 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 02d0fb5c..ecdf542b 100644 --- a/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java +++ b/src/test/java/com/descope/sdk/mgmt/impl/UserServiceImplTest.java @@ -350,7 +350,7 @@ void testDeactivateForSuccess() { @Test void testUpdateEmailForEmptyLoginId() { ServerCommonException thrown = assertThrows(ServerCommonException.class, - () -> userService.updateEmail("", "someEmail", false, false)); + () -> userService.updateEmail("", "someEmail", false)); assertNotNull(thrown); assertEquals("The Login ID argument is invalid", thrown.getMessage()); } @@ -362,7 +362,19 @@ void testUpdateEmailForSuccess() { doReturn(userResponseDetails).when(apiProxy).post(any(), any(), any()); try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); - UserResponseDetails response = userService.updateEmail("someLoginId", "someEmail", false, false); + UserResponseDetails response = userService.updateEmail("someLoginId", "someEmail", false); + Assertions.assertThat(response).isNotNull(); + } + } + + @Test + void testUpdateEmailWithFailOnConflictForSuccess() { + UserResponseDetails userResponseDetails = mock(UserResponseDetails.class); + ApiProxy apiProxy = mock(ApiProxy.class); + doReturn(userResponseDetails).when(apiProxy).post(any(), any(), any()); + try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { + mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); + UserResponseDetails response = userService.updateEmail("someLoginId", "someEmail", false, true); Assertions.assertThat(response).isNotNull(); } } @@ -370,7 +382,7 @@ void testUpdateEmailForSuccess() { @Test void testUpdatePhoneForEmptyLoginId() { ServerCommonException thrown = assertThrows(ServerCommonException.class, - () -> userService.updatePhone("", "someEmail", false, false)); + () -> userService.updatePhone("", "someEmail", false)); assertNotNull(thrown); assertEquals("The Login ID argument is invalid", thrown.getMessage()); } @@ -382,7 +394,19 @@ void testUpdatePhoneForSuccess() { doReturn(userResponseDetails).when(apiProxy).post(any(), any(), any()); try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); - UserResponseDetails response = userService.updatePhone("someLoginId", "1234567890", false, false); + UserResponseDetails response = userService.updatePhone("someLoginId", "1234567890", false); + Assertions.assertThat(response).isNotNull(); + } + } + + @Test + void testUpdatePhoneWithFailOnConflictForSuccess() { + UserResponseDetails userResponseDetails = mock(UserResponseDetails.class); + ApiProxy apiProxy = mock(ApiProxy.class); + doReturn(userResponseDetails).when(apiProxy).post(any(), any(), any()); + try (MockedStatic mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) { + mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy); + UserResponseDetails response = userService.updatePhone("someLoginId", "1234567890", false, true); Assertions.assertThat(response).isNotNull(); } }