diff --git a/app/server/src/main/java/com/appsmith/server/configurations/ClientUserRepository.java b/app/server/src/main/java/com/appsmith/server/configurations/ClientUserRepository.java index 343b0bd942..51599ecd58 100644 --- a/app/server/src/main/java/com/appsmith/server/configurations/ClientUserRepository.java +++ b/app/server/src/main/java/com/appsmith/server/configurations/ClientUserRepository.java @@ -47,6 +47,7 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit private final String sessionAttributeName = DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME; UserService userService; OrganizationService organizationService; + public ClientUserRepository(UserService userService, OrganizationService organizationService) { this.userService = userService; this.organizationService = organizationService; @@ -92,20 +93,8 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit newUser.setState(UserState.ACTIVATED); newUser.setIsEnabled(true); - /** TODO - * Organization here is being hardcoded. This is a stop gap measure - * A flow needs to be added to connect a User to a Organization. Once - * that is done, during the login, the organization should be picked up - * and a user should be hence created. - */ - - return organizationService.findById("5d3e90a2dfec7c00047a81ea") - .map(organization -> { - newUser.setOrganization(organization); - return newUser; - }) - .then(userService.findByEmail(user.getEmail())) - .switchIfEmpty(userService.save(newUser)); + return userService.findByEmail(user.getEmail()) + .switchIfEmpty(userService.save(newUser)); //In case the user doesnt exist, save the user. } @Override diff --git a/app/server/src/main/java/com/appsmith/server/constants/Url.java b/app/server/src/main/java/com/appsmith/server/constants/Url.java index 940faa9fab..ccf40b66bd 100644 --- a/app/server/src/main/java/com/appsmith/server/constants/Url.java +++ b/app/server/src/main/java/com/appsmith/server/constants/Url.java @@ -11,4 +11,5 @@ public interface Url { String SETTING_URL = BASE_URL + VERSION + "/settings"; String RESOURCE_URL = BASE_URL + VERSION + "/resources"; String ACTION_URL = BASE_URL + VERSION + "/actions"; + String USER_URL = BASE_URL + VERSION + "/users"; } diff --git a/app/server/src/main/java/com/appsmith/server/controllers/IndexController.java b/app/server/src/main/java/com/appsmith/server/controllers/IndexController.java index b3075f8dd5..f7408a1c8a 100644 --- a/app/server/src/main/java/com/appsmith/server/controllers/IndexController.java +++ b/app/server/src/main/java/com/appsmith/server/controllers/IndexController.java @@ -1,6 +1,9 @@ package com.appsmith.server.controllers; +import com.appsmith.server.domains.User; +import com.appsmith.server.services.UserService; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -13,8 +16,16 @@ import java.security.Principal; @RequestMapping("") public class IndexController { + private final UserService service; + + @Autowired + public IndexController(UserService service) { + this.service = service; + } + @GetMapping public Mono index(Mono principal) { + Mono userMono = service.getCurrentUser(); return principal .map(Principal::getName) .map(name -> String.format("Hello %s", name)); diff --git a/app/server/src/main/java/com/appsmith/server/controllers/UserController.java b/app/server/src/main/java/com/appsmith/server/controllers/UserController.java new file mode 100644 index 0000000000..4cdf76b65f --- /dev/null +++ b/app/server/src/main/java/com/appsmith/server/controllers/UserController.java @@ -0,0 +1,18 @@ +package com.appsmith.server.controllers; + +import com.appsmith.server.constants.Url; +import com.appsmith.server.domains.User; +import com.appsmith.server.services.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(Url.USER_URL) +public class UserController extends BaseController { + + @Autowired + public UserController(UserService service) { + super(service); + } +} diff --git a/app/server/src/main/java/com/appsmith/server/domains/User.java b/app/server/src/main/java/com/appsmith/server/domains/User.java index 1d2e32948d..a4802decf6 100644 --- a/app/server/src/main/java/com/appsmith/server/domains/User.java +++ b/app/server/src/main/java/com/appsmith/server/domains/User.java @@ -33,9 +33,9 @@ public class User extends BaseDomain implements UserDetails { private UserState state; - private Boolean isEnabled; + private Boolean isEnabled = true; - private Organization organization; + private String organizationId; @Override public Collection getAuthorities() { diff --git a/app/server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java b/app/server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java index 5f32ce5b82..62fbc48cbb 100644 --- a/app/server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java +++ b/app/server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java @@ -9,7 +9,7 @@ public enum AppsmithError { NO_RESOURCE_FOUND(404, 1000, "Unable to find {0} with id {1}"), INVALID_PARAMETER(400, 4000, "Invalid parameter {0} provided in the input"), - PLUGIN_NOT_INSTALLED(400, 4001, "Plugin not installed for organization {0}"), + PLUGIN_NOT_INSTALLED(400, 4001, "Plugin {0} not installed"), PLUGIN_ID_NOT_GIVEN(400, 4002, "Missing plugin id. Please input correct plugin id"), RESOURCE_ID_NOT_GIVEN(400, 4003, "Missing resource id. Please input correct resource id"), INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request"); diff --git a/app/server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java b/app/server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java index b154a39c5b..f8fa09e10d 100644 --- a/app/server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java +++ b/app/server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java @@ -24,7 +24,7 @@ public class GlobalExceptionHandler { * * @param e AppsmithException that will be caught by the function * @param exchange ServerWebExchange contract in order to extract the response and set the http status code - * @return Mono> + * @return Mono> */ @ExceptionHandler @ResponseBody @@ -40,7 +40,7 @@ public class GlobalExceptionHandler { * * @param e Exception that will be caught by the function * @param exchange ServerWebExchange contract in order to extract the response and set the http status code - * @return Mono> + * @return Mono> */ @ExceptionHandler @ResponseBody diff --git a/app/server/src/main/java/com/appsmith/server/helpers/BeanCopyUtils.java b/app/server/src/main/java/com/appsmith/server/helpers/BeanCopyUtils.java new file mode 100644 index 0000000000..a42301281e --- /dev/null +++ b/app/server/src/main/java/com/appsmith/server/helpers/BeanCopyUtils.java @@ -0,0 +1,31 @@ +package com.appsmith.server.helpers; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; + +import java.util.HashSet; +import java.util.Set; + +public final class BeanCopyUtils { + + private static String[] getNullPropertyNames(Object source) { + final BeanWrapper src = new BeanWrapperImpl(source); + java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); + + Set emptyNames = new HashSet(); + for (java.beans.PropertyDescriptor pd : pds) { + Object srcValue = src.getPropertyValue(pd.getName()); + if (srcValue == null) { + emptyNames.add(pd.getName()); + } + } + String[] result = new String[emptyNames.size()]; + return emptyNames.toArray(result); + } + + //Use Spring BeanUtils to copy and ignore null + public static void copyNewFieldValuesIntoOldObject(Object src, Object target) { + BeanUtils.copyProperties(src, target, getNullPropertyNames(src)); + } +} diff --git a/app/server/src/main/java/com/appsmith/server/services/BaseService.java b/app/server/src/main/java/com/appsmith/server/services/BaseService.java index e965d91713..ed4ab62a32 100644 --- a/app/server/src/main/java/com/appsmith/server/services/BaseService.java +++ b/app/server/src/main/java/com/appsmith/server/services/BaseService.java @@ -68,7 +68,7 @@ public abstract class BaseService getById(ID id) { - if(id == null) { + if (id == null) { return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID)); } @@ -98,12 +98,12 @@ public abstract class BaseService validateObject(T obj) { return Mono.just(obj) - .map(o -> validator.validate(o)) - .flatMap(constraint -> { - if(constraint.isEmpty()) { - return Mono.just(obj); - } - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, constraint.stream().findFirst().get().getPropertyPath())); - }); + .map(o -> validator.validate(o)) + .flatMap(constraint -> { + if (constraint.isEmpty()) { + return Mono.just(obj); + } + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, constraint.stream().findFirst().get().getPropertyPath())); + }); } } \ No newline at end of file diff --git a/app/server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java b/app/server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java index 069e56d71d..5fafb8379e 100644 --- a/app/server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java +++ b/app/server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java @@ -1,19 +1,17 @@ package com.appsmith.server.services; -import com.appsmith.server.configurations.ClientUserRepository; import com.appsmith.server.domains.Organization; import com.appsmith.server.domains.OrganizationPlugin; import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.PluginType; +import com.appsmith.server.domains.User; import com.appsmith.server.dtos.OrganizationPluginStatus; import com.appsmith.server.dtos.PluginOrgDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.repositories.PluginRepository; -import com.appsmith.server.repositories.UserRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.convert.MongoConverter; @@ -30,13 +28,9 @@ import java.util.List; public class PluginServiceImpl extends BaseService implements PluginService { private final PluginRepository pluginRepository; - private final UserRepository userRepository; private final ApplicationContext applicationContext; - private final ClientUserRepository clientUserRepository; private final OrganizationService organizationService; - - @Value("${organization.id}") - private String organizationId; + private final UserService userService; @Autowired public PluginServiceImpl(Scheduler scheduler, @@ -44,16 +38,13 @@ public class PluginServiceImpl extends BaseService uninstallPlugin(PluginOrgDTO pluginDTO) { - /*TODO - * Organization & user association is being mocked here by forcefully - * only using a hardcoded organization. This needs to be replaced by - * a user-organization association flow. The Organization needs to be picked - * up from a user object. This is being used in install/uninstall - * plugin from a organization flow. Instead, the current user should be read - * using the following : - * ReactiveSecurityContextHolder.getContext() - * .map(SecurityContext::getAuthentication) - * .map(Authentication::getPrincipal); - * Once the user has been pulled using this, organization should already - * be stored as part of user and this organization should be used to store - * the installed plugin or to delete plugin during uninstallation. - */ if (pluginDTO.getPluginId() == null) { return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN)); } //Find the organization using id and plugin id -> This is to find if the organization has the plugin installed - Mono organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, pluginDTO.getPluginId()); + Mono userMono = userService.getCurrentUser(); + Mono organizationMono = userMono.flatMap(user -> + organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), pluginDTO.getPluginId())); return organizationMono - .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, organizationId))) + .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, pluginDTO.getPluginId()))) //In case the plugin is not found for the organization, the organizationMono would not emit and the rest of the flow would stop //i.e. the rest of the code flow would only happen when there is a plugin found for the organization that can //be uninstalled. @@ -126,40 +105,33 @@ public class PluginServiceImpl extends BaseService storeOrganizationPlugin(PluginOrgDTO pluginDTO, OrganizationPluginStatus status) { - /*TODO - * Organization & user association is being mocked here by forcefully - * only using a hardcoded organization. This needs to be replaced by - * a user-organization association flow. The Organization needs to be picked - * up from a user object. This is being used in install/uninstall - * plugin from a organization flow. Instead, the current user should be read - * using the following : - * ReactiveSecurityContextHolder.getContext() - * .map(SecurityContext::getAuthentication) - * .map(Authentication::getPrincipal); - * Once the user has been pulled using this, organization should already - * be stored as part of user and this organization should be used to store - * the installed plugin or to delete plugin during uninstalling. - */ //Find the organization using id and plugin id -> This is to find if the organization already has the plugin installed - Mono organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, pluginDTO.getPluginId()); + Mono userMono = userService.getCurrentUser(); + Mono organizationMono = userMono.flatMap(user -> + organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), pluginDTO.getPluginId())); + + //If plugin is already present for the organization, just return the organization, else install and return organization return organizationMono .switchIfEmpty(Mono.defer(() -> { - //If the plugin is not found in the organization, its not already installed. Install now. - return organizationService.findById(organizationId).map(organization -> { - List organizationPluginList = organization.getPlugins(); - if (organizationPluginList == null) { - organizationPluginList = new ArrayList(); - } - log.debug("Installing plugin {} for organization {}", pluginDTO.getPluginId(), organization.getName()); - OrganizationPlugin organizationPlugin = new OrganizationPlugin(); - organizationPlugin.setPluginId(pluginDTO.getPluginId()); - organizationPlugin.setStatus(status); - organizationPluginList.add(organizationPlugin); - organization.setPlugins(organizationPluginList); - return organization; - }).flatMap(organizationService::save); + //If the plugin is not found in the organization, its not installed already. Install now. + return userMono + .flatMap(user -> organizationService.findById(user.getOrganizationId())) + .map(organization -> { + List organizationPluginList = organization.getPlugins(); + if (organizationPluginList == null) { + organizationPluginList = new ArrayList(); + } + log.debug("Installing plugin {} for organization {}", pluginDTO.getPluginId(), organization.getName()); + OrganizationPlugin organizationPlugin = new OrganizationPlugin(); + organizationPlugin.setPluginId(pluginDTO.getPluginId()); + organizationPlugin.setStatus(status); + organizationPluginList.add(organizationPlugin); + organization.setPlugins(organizationPluginList); + return organization; + }) + .flatMap(organizationService::save); })); } diff --git a/app/server/src/main/java/com/appsmith/server/services/ResourceServiceImpl.java b/app/server/src/main/java/com/appsmith/server/services/ResourceServiceImpl.java index 71bca4545c..0bc9733f7d 100644 --- a/app/server/src/main/java/com/appsmith/server/services/ResourceServiceImpl.java +++ b/app/server/src/main/java/com/appsmith/server/services/ResourceServiceImpl.java @@ -2,12 +2,12 @@ package com.appsmith.server.services; import com.appsmith.server.domains.Organization; import com.appsmith.server.domains.Resource; +import com.appsmith.server.domains.User; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.repositories.ResourceRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.stereotype.Service; @@ -21,19 +21,16 @@ import javax.validation.constraints.NotNull; @Service public class ResourceServiceImpl extends BaseService implements ResourceService { - @Value("${organization.id}") - private String organizationId; - private final ResourceRepository repository; private final OrganizationService organizationService; - private final PluginService pluginService; + private final UserService userService; @Autowired - public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService) { + public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService, UserService userService) { super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository); this.repository = repository; this.organizationService = organizationService; - this.pluginService = pluginService; + this.userService = userService; } @Override @@ -44,17 +41,19 @@ public class ResourceServiceImpl extends BaseService organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, resource.getPluginId()); + Mono userMono = userService.getCurrentUser(); + + Mono organizationMono = userMono.flatMap(user -> organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), resource.getPluginId())); //Add organization id to the resource. - Mono updatedResourceMono = Mono.just(resource) - .map(updatedResource -> { - updatedResource.setOrganizationId(organizationId); - return updatedResource; + Mono updatedResourceMono = organizationMono + .map(organization -> { + resource.setOrganizationId(organization.getId()); + return resource; }); return organizationMono - .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, organizationId))) + .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, resource.getPluginId()))) .then(updatedResourceMono) .flatMap(repository::save); } diff --git a/app/server/src/main/java/com/appsmith/server/services/UserService.java b/app/server/src/main/java/com/appsmith/server/services/UserService.java index 426a83ca9e..5377b6f17f 100644 --- a/app/server/src/main/java/com/appsmith/server/services/UserService.java +++ b/app/server/src/main/java/com/appsmith/server/services/UserService.java @@ -10,4 +10,6 @@ public interface UserService extends CrudService { Mono findByEmail(String email); Mono save(User newUser); + + Mono getCurrentUser(); } diff --git a/app/server/src/main/java/com/appsmith/server/services/UserServiceImpl.java b/app/server/src/main/java/com/appsmith/server/services/UserServiceImpl.java index 62494ed4ff..58f3d83f1d 100644 --- a/app/server/src/main/java/com/appsmith/server/services/UserServiceImpl.java +++ b/app/server/src/main/java/com/appsmith/server/services/UserServiceImpl.java @@ -1,30 +1,43 @@ package com.appsmith.server.services; import com.appsmith.server.domains.User; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.helpers.BeanCopyUtils; import com.appsmith.server.repositories.UserRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.convert.MongoConverter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import javax.validation.Validator; +@Slf4j @Service public class UserServiceImpl extends BaseService implements UserService, UserDetailsService { private UserRepository repository; + private final OrganizationService organizationService; + @Autowired public UserServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, - UserRepository repository) { + UserRepository repository, OrganizationService organizationService) { super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository); this.repository = repository; + this.organizationService = organizationService; } @Override @@ -46,4 +59,55 @@ public class UserServiceImpl extends BaseService i public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return repository.findByName(username).block(); } + + @Override + public Mono getCurrentUser() { + return ReactiveSecurityContextHolder.getContext() + .map(SecurityContext::getAuthentication) + .map(Authentication::getPrincipal) + .flatMap(principal -> { + String email; + if (principal instanceof org.springframework.security.core.userdetails.User) { + org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) principal; + //Assumption that the user has inputted an email as username during user creation and not english passport name + email = user.getUsername(); + } else { + DefaultOidcUser defaultOidcUser = (DefaultOidcUser) principal; + email = defaultOidcUser.getEmail(); + } + return repository.findByEmail(email); + }); + } + + @Override + public Mono update(String id, User userUpdate) { + Mono userFromRepository = repository.findById(id); + + return Mono.just(userUpdate) + .flatMap(this::validateUpdate) + //Once the new update has been validated, update the user with the new fields. + .then(userFromRepository) + .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, id))) + .map(existingUser -> { + BeanCopyUtils.copyNewFieldValuesIntoOldObject(userUpdate, existingUser); + return existingUser; + }) + .flatMap(repository::save); + } + + //Validation for user update. Right now it only validates the organization id. Other checks can be added + //here in the future. + private Mono validateUpdate(User updateUser) { + if (updateUser.getOrganizationId() == null) { + //No organization present implies the update to the user is not to the organization id. No checks currently + //for this scenario. Return the user successfully. + return Mono.just(updateUser); + } + return organizationService.findById(updateUser.getOrganizationId()) + //If the organization is not found in the repository, throw an error + .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, updateUser.getOrganizationId()))) + .then(Mono.just(updateUser)); + + } + } diff --git a/app/server/src/test/java/com/appsmith/server/services/UserServiceTest.java b/app/server/src/test/java/com/appsmith/server/services/UserServiceTest.java new file mode 100644 index 0000000000..2dcf356bad --- /dev/null +++ b/app/server/src/test/java/com/appsmith/server/services/UserServiceTest.java @@ -0,0 +1,99 @@ +package com.appsmith.server.services; + +import com.appsmith.server.domains.Organization; +import com.appsmith.server.domains.User; +import com.appsmith.server.domains.UserState; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class UserServiceTest { + + @Autowired + UserService userService; + + @Autowired + OrganizationService organizationService; + + Mono userMono; + Mono organizationMono; + + @Before + public void setup() { + //User init + User user = new User(); + user.setName("user test"); + user.setEmail("usertest@usertest.com"); + user.setState(UserState.ACTIVATED); + //Store the user in case its not present in the database. + userMono = userService.findByEmail("usertest@usertest.com").switchIfEmpty(Mono.defer(() -> userService.save(user))); + + //Organization init + Organization organization = new Organization(); + organization.setName("Spring Test Organization"); + organization.setDomain("appsmith-spring-test.com"); + organization.setWebsite("appsmith.com"); + + //Store the organization in case its not present in the database. + organizationMono = organizationService.getByName("Spring Test Organization").switchIfEmpty(Mono.defer(() -> organizationService.save(organization))); + } + + //Test the update organization flow. + + @Test + public void updateInvalidUserWithAnything() { + User updateUser = new User(); + updateUser.setName("Random User whose updates don't matter"); + + User existingUser = new User(); + existingUser.setId("Random-UserId-%Not-In_The-System_For_SUre"); + + Mono userMono1 = Mono.just(existingUser).flatMap(user -> userService.update(user.getId(), updateUser)); + + StepVerifier.create(userMono1) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage("Random-UserId-%Not-In_The-System_For_SUre"))) + .verify(); + } + + @Test + public void updateUserWithValidOrganization() { + User updateUser = new User(); + //Add valid organization id to the updateUser object. + organizationMono + .map(organization -> { + updateUser.setOrganizationId(organization.getId()); + return updateUser; + }).block(); + + Mono userMono1 = userMono.flatMap(user -> userService.update(user.getId(), updateUser)); + StepVerifier.create(userMono1) + .assertNext(updatedUserInRepository -> { + assertThat(updatedUserInRepository.getOrganizationId()).isEqualTo(updateUser.getOrganizationId()); + }) + .verifyComplete(); + } + + @Test + public void updateUserWithInvalidOrganization() { + User updateUser = new User(); + updateUser.setOrganizationId("Random-OrgId-%Not-In_The-System_For_SUre"); + Mono userMono1 = userMono.flatMap(user -> userService.update(user.getId(), updateUser)); + StepVerifier.create(userMono1) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage("Random-OrgId-%Not-In_The-System_For_SUre"))) + .verify(); + } + +}