diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/projections/EmailOnly.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/projections/EmailOnly.java new file mode 100644 index 0000000000..c7a4652a97 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/projections/EmailOnly.java @@ -0,0 +1,5 @@ +package com.appsmith.server.projections; + +import lombok.NonNull; + +public record EmailOnly(@NonNull String email) {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCE.java index 8498bd8457..ceb13a7b82 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCE.java @@ -3,17 +3,16 @@ package com.appsmith.server.repositories.ce; import com.appsmith.server.domains.User; import com.appsmith.server.repositories.AppsmithRepository; import org.springframework.data.mongodb.core.query.UpdateDefinition; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.Set; - public interface CustomUserRepositoryCE extends AppsmithRepository { Mono findByEmailAndOrganizationId(String email, String organizationId); Mono isUsersEmpty(); - Set getSystemGeneratedUserEmails(); + Flux getSystemGeneratedUserEmails(String organizationId); Mono updateById(String id, UpdateDefinition updateObj); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImpl.java index 411c78606b..a21d5f7ec0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImpl.java @@ -5,14 +5,15 @@ import com.appsmith.server.domains.User; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.ce.bridge.Bridge; +import com.appsmith.server.projections.EmailOnly; import com.appsmith.server.projections.IdOnly; import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.data.mongodb.core.query.UpdateDefinition; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.HashSet; -import java.util.Set; +import static com.appsmith.server.helpers.ce.bridge.Bridge.notExists; @Slf4j public class CustomUserRepositoryCEImpl extends BaseAppsmithRepositoryImpl implements CustomUserRepositoryCE { @@ -25,7 +26,8 @@ public class CustomUserRepositoryCEImpl extends BaseAppsmithRepositoryImpl } /** - * Fetch minimal information from *a* user document in the database, limit to two documents, filter anonymousUser + * Fetch minimal information from *a* user document in the database, limit to two documents, filter system generated + * users. * If no documents left return true otherwise return false. * * @return Boolean, indicated where there exists at least one user in the system or not. @@ -33,7 +35,8 @@ public class CustomUserRepositoryCEImpl extends BaseAppsmithRepositoryImpl @Override public Mono isUsersEmpty() { return queryBuilder() - .criteria(Bridge.notIn(User.Fields.email, getSystemGeneratedUserEmails())) + .criteria(Bridge.or( + notExists(User.Fields.isSystemGenerated), Bridge.isFalse(User.Fields.isSystemGenerated))) .limit(1) .all(IdOnly.class) .count() @@ -41,10 +44,12 @@ public class CustomUserRepositoryCEImpl extends BaseAppsmithRepositoryImpl } @Override - public Set getSystemGeneratedUserEmails() { - Set systemGeneratedEmails = new HashSet<>(); - systemGeneratedEmails.add(FieldName.ANONYMOUS_USER); - return systemGeneratedEmails; + public Flux getSystemGeneratedUserEmails(String organizationId) { + return queryBuilder() + .criteria(Bridge.equal(User.Fields.isSystemGenerated, true) + .equal(User.Fields.organizationId, organizationId)) + .all(EmailOnly.class) + .map(EmailOnly::email); } @Override diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImplTest.java new file mode 100644 index 0000000000..c7b8aa396a --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/repositories/ce/CustomUserRepositoryCEImplTest.java @@ -0,0 +1,128 @@ +package com.appsmith.server.repositories.ce; + +import com.appsmith.server.domains.Organization; +import com.appsmith.server.domains.User; +import com.appsmith.server.repositories.OrganizationRepository; +import com.appsmith.server.repositories.UserRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class CustomUserRepositoryCEImplTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private OrganizationRepository organizationRepository; + + private final List savedUsers = new ArrayList<>(); + private static final Set PREDEFINED_SYSTEM_USERS = Set.of("anonymousUser"); + + @BeforeEach + public void setUp() { + this.savedUsers.clear(); + } + + @AfterEach + public void cleanUp() { + for (User savedUser : savedUsers) { + userRepository.deleteById(savedUser.getId()).block(); + } + } + + @Test + public void getSystemGeneratedUserEmails_WhenSystemGeneratedUsersExist_ReturnsTheirEmails() { + String defaultOrgId = organizationRepository + .findBySlug("default") + .map(Organization::getId) + .block(); + + // Create an additional system-generated user + User systemUser = new User(); + systemUser.setEmail("system@test.com"); + systemUser.setIsSystemGenerated(true); + systemUser.setOrganizationId(defaultOrgId); + User savedSystemUser = userRepository.save(systemUser).block(); + savedUsers.add(savedSystemUser); + + // Create a non-system-generated user + User nonSystemUser = new User(); + nonSystemUser.setEmail("non-system@test.com"); + nonSystemUser.setIsSystemGenerated(false); + nonSystemUser.setOrganizationId(defaultOrgId); + User savedNonSystemUser = userRepository.save(nonSystemUser).block(); + savedUsers.add(savedNonSystemUser); + + // Test the method + Flux systemGeneratedEmails = userRepository.getSystemGeneratedUserEmails(defaultOrgId); + + StepVerifier.create(systemGeneratedEmails.collectList()) + .assertNext(emails -> { + // Verify all predefined system users are present + assertThat(emails).containsAll(PREDEFINED_SYSTEM_USERS); + // Verify our test system user is present + assertThat(emails).contains("system@test.com"); + // Verify non-system user is not present + assertThat(emails).doesNotContain("non-system@test.com"); + // Verify total count is correct (predefined users + our test user) + assertThat(emails).hasSize(PREDEFINED_SYSTEM_USERS.size() + 1); + }) + .verifyComplete(); + } + + @Test + public void getSystemGeneratedUserEmails_WhenNoAdditionalSystemGeneratedUsersExist_ReturnsOnlyPredefinedUsers() { + String defaultOrgId = organizationRepository + .findBySlug("default") + .map(Organization::getId) + .block(); + + // Create only non-system-generated user + User nonSystemUser = new User(); + nonSystemUser.setEmail("non-system@test.com"); + nonSystemUser.setIsSystemGenerated(false); + nonSystemUser.setOrganizationId(defaultOrgId); + User savedNonSystemUser = userRepository.save(nonSystemUser).block(); + savedUsers.add(savedNonSystemUser); + + // Test the method + Flux systemGeneratedEmails = userRepository.getSystemGeneratedUserEmails(defaultOrgId); + + StepVerifier.create(systemGeneratedEmails.collectList()) + .assertNext(emails -> { + // Verify only predefined system users are present + assertThat(emails).containsExactlyInAnyOrderElementsOf(PREDEFINED_SYSTEM_USERS); + // Verify non-system user is not present + assertThat(emails).doesNotContain("non-system@test.com"); + }) + .verifyComplete(); + } + + @Test + public void isUsersEmpty_WhenNonSystemUsersExist_ReturnsFalse() { + // Create a non-system-generated user + User nonSystemUser = new User(); + nonSystemUser.setEmail("non-system@test.com"); + User savedNonSystemUser = userRepository.save(nonSystemUser).block(); + savedUsers.add(savedNonSystemUser); + + // Test the method + StepVerifier.create(userRepository.isUsersEmpty()) + .assertNext(isEmpty -> { + assertThat(isEmpty).isFalse(); + }) + .verifyComplete(); + } +}