chore: Update instance variables via organization config update (#39856)

## Description
Changes made in this PR are temporary and will be removed once we have
seperate API to fetch instance config. This is just to enable the
skipped tests
[theappsmith.slack.com/archives/C0134BAVDB4/p1742273278029189](https://theappsmith.slack.com/archives/C0134BAVDB4/p1742273278029189).

Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

/test Settings

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/13991283921>
> Commit: 648757fdda271d00159c45d2b4a9eaab06140b0c
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13991283921&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Settings`
> Spec:
> <hr>Fri, 21 Mar 2025 12:22:32 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced organization settings management with streamlined instance
variable updates.
- **Bug Fixes**
- Improved email verification handling to ensure robust, null-safe
behavior.
- **Refactor**
- Optimized workflows for updating organization configurations for more
immediate and consistent updates.
- **Tests**
- Updated test routines to validate the refined configuration and email
verification processes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Abhijeet 2025-03-24 11:03:55 +05:30 committed by GitHub
parent c505c5cf59
commit 5c0562478c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 84 additions and 27 deletions

View File

@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.experimental.FieldNameConstants;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.annotation.Transient;
import java.io.Serializable;
import java.util.ArrayList;
@ -20,17 +21,20 @@ import java.util.Map;
@FieldNameConstants
public class OrganizationConfigurationCE implements Serializable {
@Transient
@Deprecated(forRemoval = true, since = "v1.65")
private String googleMapsKey;
private Boolean isFormLoginEnabled;
@Transient
@Deprecated(forRemoval = true, since = "v1.65")
private String instanceName;
protected License license;
// organization admin can toggle this field to enable/disable email verification
@Transient
@Deprecated(forRemoval = true, since = "v1.65")
private Boolean emailVerificationEnabled;
@ -82,7 +86,7 @@ public class OrganizationConfigurationCE implements Serializable {
ObjectUtils.defaultIfNull(organizationConfiguration.getIsFormLoginEnabled(), isFormLoginEnabled);
instanceName = ObjectUtils.defaultIfNull(organizationConfiguration.getInstanceName(), instanceName);
emailVerificationEnabled = ObjectUtils.defaultIfNull(
organizationConfiguration.isEmailVerificationEnabled(), emailVerificationEnabled);
organizationConfiguration.getEmailVerificationEnabled(), emailVerificationEnabled);
featuresWithPendingMigration = organizationConfiguration.getFeaturesWithPendingMigration();
migrationStatus = organizationConfiguration.getMigrationStatus();
@ -90,10 +94,6 @@ public class OrganizationConfigurationCE implements Serializable {
isAtomicPushAllowed = organizationConfiguration.getIsAtomicPushAllowed();
}
public Boolean isEmailVerificationEnabled() {
return Boolean.TRUE.equals(this.emailVerificationEnabled);
}
public static class Fields {
public Fields() {
// Public constructor for Fields class

View File

@ -1,10 +1,15 @@
package com.appsmith.server.helpers.ce;
import com.appsmith.server.constants.Appsmith;
import com.appsmith.server.domains.OrganizationConfiguration;
import com.appsmith.server.services.ConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* Helper class for accessing instance variables from the instance config
*/
@ -47,4 +52,47 @@ public class InstanceVariablesHelperCE {
return value != null ? value.toString() : "";
});
}
public OrganizationConfiguration populateOrgConfigWithInstanceVariables(
Map<String, Object> instanceVariables, OrganizationConfiguration organizationConfiguration) {
Object value = instanceVariables.getOrDefault("instanceName", Appsmith.DEFAULT_INSTANCE_NAME);
organizationConfiguration.setInstanceName(value != null ? value.toString() : Appsmith.DEFAULT_INSTANCE_NAME);
value = instanceVariables.getOrDefault("emailVerificationEnabled", false);
if (value instanceof Boolean) {
organizationConfiguration.setEmailVerificationEnabled((Boolean) value);
} else {
organizationConfiguration.setEmailVerificationEnabled(Boolean.FALSE);
}
value = instanceVariables.getOrDefault("googleMapsKey", "");
organizationConfiguration.setGoogleMapsKey(value != null ? value.toString() : "");
return organizationConfiguration;
}
// TODO @CloudBilling: Temporary method to update instance variables via organization configuration. This method
// will be removed once the instance variables will be removed from organization configuration
public Mono<OrganizationConfiguration> updateInstanceVariables(OrganizationConfiguration orgConfig) {
Map<String, Object> updatedInstanceVariables = updateAllowedInstanceVariables(orgConfig);
return configService.getInstanceVariables().flatMap(instanceVariable -> {
instanceVariable.putAll(updatedInstanceVariables);
return configService.updateInstanceVariables(instanceVariable).thenReturn(orgConfig);
});
}
protected Map<String, Object> updateAllowedInstanceVariables(OrganizationConfiguration orgConfig) {
Map<String, Object> instanceVariables = new HashMap<>();
if (StringUtils.hasLength(orgConfig.getInstanceName())) {
instanceVariables.put("instanceName", orgConfig.getInstanceName());
}
if (orgConfig.getEmailVerificationEnabled() != null) {
instanceVariables.put("emailVerificationEnabled", orgConfig.getEmailVerificationEnabled());
}
if (StringUtils.hasLength(orgConfig.getGoogleMapsKey())) {
instanceVariables.put("googleMapsKey", orgConfig.getGoogleMapsKey());
}
return instanceVariables;
}
}

View File

@ -70,7 +70,7 @@ public class Migration069MigrateOrganizationConfigToInstanceConfig {
instanceVariables.put("instanceName", Appsmith.DEFAULT_INSTANCE_NAME);
}
instanceVariables.put("emailVerificationEnabled", orgConfig.isEmailVerificationEnabled());
instanceVariables.put("emailVerificationEnabled", Boolean.TRUE.equals(orgConfig.getEmailVerificationEnabled()));
instanceVariables.put("googleMapsKey", orgConfig.getGoogleMapsKey());
// Add instanceVariables to config

View File

@ -3,7 +3,6 @@ package com.appsmith.server.services.ce;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.domains.Config;
import com.appsmith.server.domains.User;
import net.minidev.json.JSONObject;
import reactor.core.publisher.Mono;
import java.util.Map;
@ -37,5 +36,5 @@ public interface ConfigServiceCE {
* @param instanceVariables JSONObject containing the instance variables to update
* @return Updated Config object
*/
Mono<Config> updateInstanceVariables(JSONObject instanceVariables);
Mono<Config> updateInstanceVariables(Map<String, Object> instanceVariables);
}

View File

@ -114,13 +114,13 @@ public class ConfigServiceCEImpl implements ConfigServiceCE {
}
@Override
public Mono<Config> updateInstanceVariables(JSONObject instanceVariables) {
return getByName(FieldName.INSTANCE_CONFIG, AclPermission.MANAGE_INSTANCE_CONFIGURATION)
.flatMap(config -> {
JSONObject configObj = config.getConfig();
configObj.put(INSTANCE_VARIABLES, instanceVariables);
config.setConfig(configObj);
return repository.save(config);
});
public Mono<Config> updateInstanceVariables(Map<String, Object> instanceVariables) {
// TODO @CloudBilling add manage instance permission once the migration for variables is complete
return getByName(FieldName.INSTANCE_CONFIG).flatMap(config -> {
JSONObject configObj = config.getConfig();
configObj.put(INSTANCE_VARIABLES, instanceVariables);
config.setConfig(configObj);
return repository.save(config);
});
}
}

View File

@ -103,7 +103,7 @@ public class OrganizationServiceCEImpl extends BaseService<OrganizationRepositor
}
Mono<Map<String, String>> envMono = Mono.empty();
// instance admin is setting the email verification to true but the SMTP settings are not configured
if (organizationConfiguration.isEmailVerificationEnabled() == Boolean.TRUE) {
if (Boolean.TRUE.equals(organizationConfiguration.getEmailVerificationEnabled())) {
envMono = envManager.getAllNonEmpty().flatMap(properties -> {
String mailHost = properties.get("APPSMITH_MAIL_HOST");
if (mailHost == null || mailHost == "") {
@ -128,10 +128,12 @@ public class OrganizationServiceCEImpl extends BaseService<OrganizationRepositor
Mono<Organization> updatedOrganizationMono = repository
.updateById(organizationId, organization, MANAGE_ORGANIZATION)
.cache();
// Firstly updating the Organization object in the database and then evicting the cache.
// returning the updatedOrganization, notice the updatedOrganizationMono is cached using .cache()
// hence it will not be evaluated again
return updatedOrganizationMono
.then(instanceVariablesHelper.updateInstanceVariables(organizationConfiguration))
.then(Mono.defer(() -> evictOrganizationCache))
.then(Mono.defer(() -> allSideEffectsMono))
.then(updatedOrganizationMono);
@ -154,12 +156,10 @@ public class OrganizationServiceCEImpl extends BaseService<OrganizationRepositor
@Override
public Mono<Organization> getOrganizationConfiguration(Mono<Organization> dbOrganizationMono) {
String adminEmailDomainHash = commonConfig.getAdminEmailDomainHash();
Mono<Organization> clientOrganizationMono = Mono.zip(
configService.getInstanceId(), instanceVariablesHelper.getGoogleMapsKey())
.map(tuple -> {
final String instanceId = tuple.getT1();
final String googleMapsKey = tuple.getT2();
Mono<Organization> clientOrganizationMono = configService
.getInstanceId()
.flatMap(instanceId -> {
final Organization organization = new Organization();
organization.setInstanceId(instanceId);
organization.setAdminEmailDomainHash(adminEmailDomainHash);
@ -167,8 +167,6 @@ public class OrganizationServiceCEImpl extends BaseService<OrganizationRepositor
final OrganizationConfiguration config = new OrganizationConfiguration();
organization.setOrganizationConfiguration(config);
config.setGoogleMapsKey(googleMapsKey);
if (StringUtils.hasText(System.getenv("APPSMITH_OAUTH2_GOOGLE_CLIENT_ID"))) {
config.addThirdPartyAuth("google");
}
@ -179,7 +177,14 @@ public class OrganizationServiceCEImpl extends BaseService<OrganizationRepositor
config.setIsFormLoginEnabled(!"true".equals(System.getenv("APPSMITH_FORM_LOGIN_DISABLED")));
return organization;
return configService
.getInstanceVariables()
.map(instanceVariables -> instanceVariablesHelper.populateOrgConfigWithInstanceVariables(
instanceVariables, config))
.map(organizationConfiguration -> {
organization.setOrganizationConfiguration(organizationConfiguration);
return organization;
});
});
return Mono.zip(dbOrganizationMono, clientOrganizationMono).flatMap(tuple -> {

View File

@ -13,6 +13,7 @@ import com.appsmith.server.helpers.ce.bridge.Bridge;
import com.appsmith.server.repositories.CacheableRepositoryHelper;
import com.appsmith.server.repositories.OrganizationRepository;
import com.appsmith.server.repositories.UserRepository;
import com.appsmith.server.services.ConfigService;
import com.appsmith.server.services.FeatureFlagService;
import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.solutions.EnvManager;
@ -81,6 +82,9 @@ class OrganizationServiceCETest {
@MockBean
FeatureFlagMigrationHelper featureFlagMigrationHelper;
@Autowired
ConfigService configService;
OrganizationConfiguration originalOrganizationConfiguration;
@BeforeEach
@ -97,6 +101,7 @@ class OrganizationServiceCETest {
null)
.block();
configService.updateInstanceVariables(new HashMap<>()).block();
// Make api_user super-user to test organization admin functionality
// Todo change this to organization admin once we introduce multitenancy
userRepository
@ -230,7 +235,7 @@ class OrganizationServiceCETest {
StepVerifier.create(resultMono)
.assertNext(organizationConfiguration -> {
assertThat(organizationConfiguration.isEmailVerificationEnabled())
assertThat(organizationConfiguration.getEmailVerificationEnabled())
.isTrue();
})
.verifyComplete();
@ -249,7 +254,7 @@ class OrganizationServiceCETest {
StepVerifier.create(resultMono)
.assertNext(organizationConfiguration -> {
assertThat(organizationConfiguration.isEmailVerificationEnabled())
assertThat(organizationConfiguration.getEmailVerificationEnabled())
.isFalse();
})
.verifyComplete();