Add API for updating role and goal for users (#8242)

This commit is contained in:
Shrikant Sharat Kandula 2021-10-08 09:40:19 +05:30 committed by GitHub
parent 470fab30f7
commit a4975c5bff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 120 additions and 26 deletions

View File

@ -9,6 +9,7 @@ import com.appsmith.server.dtos.ResetUserPasswordDTO;
import com.appsmith.server.dtos.ResponseDTO;
import com.appsmith.server.dtos.UserProfileDTO;
import com.appsmith.server.dtos.UserSignupRequestDTO;
import com.appsmith.server.dtos.UserUpdateDTO;
import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.services.UserDataService;
import com.appsmith.server.services.UserOrganizationService;
@ -92,8 +93,8 @@ public class UserController extends BaseController<UserService, User, String> {
}
@PutMapping()
public Mono<ResponseDTO<User>> update(@RequestBody User resource, ServerWebExchange exchange) {
return service.updateCurrentUser(resource, exchange)
public Mono<ResponseDTO<User>> update(@RequestBody UserUpdateDTO updates, ServerWebExchange exchange) {
return service.updateCurrentUser(updates, exchange)
.map(updatedUser -> new ResponseDTO<>(HttpStatus.OK.value(), updatedUser, null));
}

View File

@ -32,6 +32,10 @@ public class UserProfileDTO {
CommentOnboardingState commentOnboardingState;
String role;
String useCase;
public boolean isAccountNonExpired() {
return this.isEnabled;
}

View File

@ -0,0 +1,25 @@
package com.appsmith.server.dtos;
import lombok.Data;
/**
* Includes **only** those fields that can be updated for a user, via an API call.
*/
@Data
public class UserUpdateDTO {
private String name;
private String role;
private String useCase;
public boolean hasUserUpdates() {
return name != null;
}
public boolean hasUserDataUpdates() {
return role != null || useCase != null;
}
}

View File

@ -18,6 +18,8 @@ public interface UserDataService {
Mono<UserData> getForUserEmail(String email);
Mono<UserData> updateForCurrentUser(UserData updates);
Mono<UserData> updateForUser(User user, UserData updates);
Mono<User> setViewedCurrentVersionReleaseNotes(User user);

View File

@ -100,20 +100,16 @@ public class UserDataServiceImpl extends BaseService<UserDataRepository, UserDat
.flatMap(this::getForUser);
}
private Mono<UserData> updateForCurrentUser(UserData updates) {
@Override
public Mono<UserData> updateForCurrentUser(UserData updates) {
return sessionUserService.getCurrentUser()
.flatMap(user -> userRepository.findByEmail(user.getEmail()))
.flatMap(user -> {
// If a UserData document exists for this user, update it. If not, create one.
updates.setUserId(user.getId());
final Mono<UserData> updaterMono = update(user.getId(), updates);
final Mono<UserData> creatorMono = Mono.just(updates).flatMap(this::create);
return updaterMono.switchIfEmpty(creatorMono);
});
.flatMap(user -> updateForUser(user, updates));
}
@Override
public Mono<UserData> updateForUser(User user, UserData updates) {
// If a UserData document exists for this user, update it. If not, create one.
updates.setUserId(user.getId());
final Mono<UserData> updaterMono = update(user.getId(), updates);
final Mono<UserData> creatorMono = Mono.just(updates).flatMap(this::create);

View File

@ -7,6 +7,7 @@ import com.appsmith.server.dtos.InviteUsersDTO;
import com.appsmith.server.dtos.ResetUserPasswordDTO;
import com.appsmith.server.dtos.UserProfileDTO;
import com.appsmith.server.dtos.UserSignupDTO;
import com.appsmith.server.dtos.UserUpdateDTO;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@ -33,7 +34,7 @@ public interface UserService extends CrudService<User, String> {
Mono<List<User>> inviteUsers(InviteUsersDTO inviteUsersDTO, String originHeader);
Mono<User> updateCurrentUser(User updates, ServerWebExchange exchange);
Mono<User> updateCurrentUser(UserUpdateDTO updates, ServerWebExchange exchange);
Map<String, String> getEmailParams(Organization organization, User inviterUser, String inviteUrl, boolean isNewUser);

View File

@ -24,6 +24,7 @@ import com.appsmith.server.dtos.InviteUsersDTO;
import com.appsmith.server.dtos.ResetUserPasswordDTO;
import com.appsmith.server.dtos.UserProfileDTO;
import com.appsmith.server.dtos.UserSignupDTO;
import com.appsmith.server.dtos.UserUpdateDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PolicyUtils;
@ -818,19 +819,37 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
}
@Override
public Mono<User> updateCurrentUser(User allUpdates, ServerWebExchange exchange) {
// Not all fields can be updated. We only let a few fields of a User object be updated with this method.
final User allowedUpdates = new User();
allowedUpdates.setName(allUpdates.getName());
public Mono<User> updateCurrentUser(final UserUpdateDTO allUpdates, ServerWebExchange exchange) {
List<Mono<Void>> monos = new ArrayList<>();
return sessionUserService.getCurrentUser()
.flatMap(user ->
update(user.getEmail(), allowedUpdates, fieldName(QUser.user.email))
.then(exchange == null
? repository.findByEmail(user.getEmail())
: sessionUserService.refreshCurrentUser(exchange))
)
.map(userChangedHandler::publish);
Mono<User> updatedUserMono;
if (allUpdates.hasUserUpdates()) {
final User updates = new User();
updates.setName(allUpdates.getName());
updatedUserMono = sessionUserService.getCurrentUser()
.flatMap(user ->
update(user.getEmail(), updates, fieldName(QUser.user.email))
.then(exchange == null
? repository.findByEmail(user.getEmail())
: sessionUserService.refreshCurrentUser(exchange))
)
.map(userChangedHandler::publish)
.cache();
monos.add(updatedUserMono.then());
} else {
updatedUserMono = sessionUserService.getCurrentUser()
.flatMap(user -> findByEmail(user.getEmail()));
}
if (allUpdates.hasUserDataUpdates()) {
final UserData updates = new UserData();
updates.setRole(allUpdates.getRole());
updates.setUseCase(allUpdates.getUseCase());
monos.add(userDataService.updateForCurrentUser(updates).then());
}
return Mono.whenDelayError(monos).then(updatedUserMono);
}
public Map<String, String> getEmailParams(Organization organization, User inviter, String inviteUrl, boolean isNewUser) {
@ -882,6 +901,8 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
profile.setAnonymous(user.isAnonymous());
profile.setEnabled(user.isEnabled());
profile.setCommentOnboardingState(userData.getCommentOnboardingState());
profile.setRole(userData.getRole());
profile.setUseCase(userData.getUseCase());
profile.setSuperUser(policyUtils.isPermissionPresentForUser(
userFromDb.getPolicies(),

View File

@ -11,9 +11,11 @@ import com.appsmith.server.domains.LoginSource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.PasswordResetToken;
import com.appsmith.server.domains.User;
import com.appsmith.server.domains.UserData;
import com.appsmith.server.dtos.InviteUsersDTO;
import com.appsmith.server.dtos.ResetUserPasswordDTO;
import com.appsmith.server.dtos.UserSignupDTO;
import com.appsmith.server.dtos.UserUpdateDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PasswordResetTokenRepository;
@ -39,6 +41,7 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.util.function.Tuple2;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
@ -83,6 +86,9 @@ public class UserServiceTest {
@Autowired
EncryptionService encryptionService;
@Autowired
UserDataService userDataService;
@MockBean
PasswordResetTokenRepository passwordResetTokenRepository;
@ -457,10 +463,8 @@ public class UserServiceTest {
@Test
@WithUserDetails(value = "api_user")
public void updateNameOfUser() {
User updateUser = new User();
updateUser.setEmail("api_user");
UserUpdateDTO updateUser = new UserUpdateDTO();
updateUser.setName("New name of api_user");
StepVerifier.create(userService.updateCurrentUser(updateUser, null))
.assertNext(user -> {
assertNotNull(user);
@ -470,6 +474,46 @@ public class UserServiceTest {
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void updateRoleOfUser() {
UserUpdateDTO updateUser = new UserUpdateDTO();
updateUser.setRole("New role of user");
final Mono<UserData> resultMono = userService.updateCurrentUser(updateUser, null)
.then(userDataService.getForUserEmail("api_user"));
StepVerifier.create(resultMono)
.assertNext(userData -> {
assertNotNull(userData);
assertThat(userData.getRole()).isEqualTo("New role of user");
})
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void updateNameRoleAndUseCaseOfUser() {
UserUpdateDTO updateUser = new UserUpdateDTO();
updateUser.setName("New name of user here");
updateUser.setRole("New role of user");
updateUser.setUseCase("New use case");
final Mono<Tuple2<User, UserData>> resultMono = userService.updateCurrentUser(updateUser, null)
.flatMap(user -> Mono.zip(
Mono.just(user),
userDataService.getForUserEmail("api_user")
));
StepVerifier.create(resultMono)
.assertNext(tuple -> {
final User user = tuple.getT1();
final UserData userData = tuple.getT2();
assertNotNull(user);
assertNotNull(userData);
assertThat(user.getName()).isEqualTo("New name of user here");
assertThat(userData.getRole()).isEqualTo("New role of user");
assertThat(userData.getUseCase()).isEqualTo("New use case");
})
.verifyComplete();
}
@Test
public void createUserAndSendEmail_WhenUserExistsWithEmailInOtherCase_ThrowsException() {
User existingUser = new User();