chore: Dynamically fetch system generated users via DB query (#40323)

This commit is contained in:
Abhijeet 2025-04-22 13:33:02 +05:30 committed by GitHub
parent f7e8aada2e
commit 988537f805
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 148 additions and 11 deletions

View File

@ -0,0 +1,5 @@
package com.appsmith.server.projections;
import lombok.NonNull;
public record EmailOnly(@NonNull String email) {}

View File

@ -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<User> {
Mono<User> findByEmailAndOrganizationId(String email, String organizationId);
Mono<Boolean> isUsersEmpty();
Set<String> getSystemGeneratedUserEmails();
Flux<String> getSystemGeneratedUserEmails(String organizationId);
Mono<Integer> updateById(String id, UpdateDefinition updateObj);
}

View File

@ -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<User> implements CustomUserRepositoryCE {
@ -25,7 +26,8 @@ public class CustomUserRepositoryCEImpl extends BaseAppsmithRepositoryImpl<User>
}
/**
* 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<User>
@Override
public Mono<Boolean> 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<User>
}
@Override
public Set<String> getSystemGeneratedUserEmails() {
Set<String> systemGeneratedEmails = new HashSet<>();
systemGeneratedEmails.add(FieldName.ANONYMOUS_USER);
return systemGeneratedEmails;
public Flux<String> getSystemGeneratedUserEmails(String organizationId) {
return queryBuilder()
.criteria(Bridge.equal(User.Fields.isSystemGenerated, true)
.equal(User.Fields.organizationId, organizationId))
.all(EmailOnly.class)
.map(EmailOnly::email);
}
@Override

View File

@ -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<User> savedUsers = new ArrayList<>();
private static final Set<String> 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<String> 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<String> 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();
}
}