fix: generate crud run behaviour updates (#40792)
## Description This PR ensures reactivity aspects are enabled for generate page from data flow as well. With these changes whenever we generate a page from data, we need to check the reactivity feature flag: if it's enabled, generated actions which are bound to widgets become automatic. If it's disabled, generated actions which are bound to widgets become page load. **Steps to test** 1. On the DP, sign up with any @integration-appsmith.com email domain and generate page from data, check the select query for its run behaviour -> It should be automatic 2. On the DP, sign up with any other email and generate page from data, check the select query for its run behaviour -> It should be page load Fixes #40789 _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Sanity" ### 🔍 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/15342086197> > Commit: e07e20226a64406a281eb1ad6cf1277bd46186ae > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=15342086197&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Sanity` > Spec: > <hr>Fri, 30 May 2025 08:37:41 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 ## Summary by CodeRabbit - **New Features** - The run behavior of certain actions (SelectQuery, FindQuery, ListFiles) when creating CRUD pages from database tables now adapts based on a feature flag. If enabled, actions run automatically; if not, they run on page load as before. - **Tests** - Added new tests to verify correct action run behavior for both enabled and disabled feature flag scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: “sneha122” <“sneha@appsmith.com”>
This commit is contained in:
parent
fccba12b4f
commit
785dfdd55a
|
|
@ -10,6 +10,7 @@ import com.appsmith.server.newpages.base.NewPageService;
|
||||||
import com.appsmith.server.plugins.base.PluginService;
|
import com.appsmith.server.plugins.base.PluginService;
|
||||||
import com.appsmith.server.services.AnalyticsService;
|
import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.ApplicationPageService;
|
import com.appsmith.server.services.ApplicationPageService;
|
||||||
|
import com.appsmith.server.services.FeatureFlagService;
|
||||||
import com.appsmith.server.services.LayoutActionService;
|
import com.appsmith.server.services.LayoutActionService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
import com.appsmith.server.solutions.ce.CreateDBTablePageSolutionCEImpl;
|
import com.appsmith.server.solutions.ce.CreateDBTablePageSolutionCEImpl;
|
||||||
|
|
@ -37,7 +38,8 @@ public class CreateDBTablePageSolutionImpl extends CreateDBTablePageSolutionCEIm
|
||||||
PagePermission pagePermission,
|
PagePermission pagePermission,
|
||||||
DatasourceStructureSolution datasourceStructureSolution,
|
DatasourceStructureSolution datasourceStructureSolution,
|
||||||
EnvironmentPermission environmentPermission,
|
EnvironmentPermission environmentPermission,
|
||||||
JsonSchemaMigration jsonSchemaMigration) {
|
JsonSchemaMigration jsonSchemaMigration,
|
||||||
|
FeatureFlagService featureFlagService) {
|
||||||
super(
|
super(
|
||||||
datasourceService,
|
datasourceService,
|
||||||
datasourceStorageService,
|
datasourceStorageService,
|
||||||
|
|
@ -55,6 +57,7 @@ public class CreateDBTablePageSolutionImpl extends CreateDBTablePageSolutionCEIm
|
||||||
pagePermission,
|
pagePermission,
|
||||||
datasourceStructureSolution,
|
datasourceStructureSolution,
|
||||||
environmentPermission,
|
environmentPermission,
|
||||||
jsonSchemaMigration);
|
jsonSchemaMigration,
|
||||||
|
featureFlagService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.ActionCreationSourceTypeEnum;
|
||||||
import com.appsmith.external.constants.AnalyticsEvents;
|
import com.appsmith.external.constants.AnalyticsEvents;
|
||||||
import com.appsmith.external.converters.HttpMethodConverter;
|
import com.appsmith.external.converters.HttpMethodConverter;
|
||||||
import com.appsmith.external.converters.ISOStringToInstantConverter;
|
import com.appsmith.external.converters.ISOStringToInstantConverter;
|
||||||
|
import com.appsmith.external.enums.FeatureFlagEnum;
|
||||||
import com.appsmith.external.git.constants.ce.RefType;
|
import com.appsmith.external.git.constants.ce.RefType;
|
||||||
import com.appsmith.external.helpers.AppsmithBeanUtils;
|
import com.appsmith.external.helpers.AppsmithBeanUtils;
|
||||||
import com.appsmith.external.models.ActionConfiguration;
|
import com.appsmith.external.models.ActionConfiguration;
|
||||||
|
|
@ -39,6 +40,7 @@ import com.appsmith.server.newpages.base.NewPageService;
|
||||||
import com.appsmith.server.plugins.base.PluginService;
|
import com.appsmith.server.plugins.base.PluginService;
|
||||||
import com.appsmith.server.services.AnalyticsService;
|
import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.ApplicationPageService;
|
import com.appsmith.server.services.ApplicationPageService;
|
||||||
|
import com.appsmith.server.services.FeatureFlagService;
|
||||||
import com.appsmith.server.services.LayoutActionService;
|
import com.appsmith.server.services.LayoutActionService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
import com.appsmith.server.solutions.ApplicationPermission;
|
import com.appsmith.server.solutions.ApplicationPermission;
|
||||||
|
|
@ -96,6 +98,7 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
|
||||||
private final DatasourceStructureSolution datasourceStructureSolution;
|
private final DatasourceStructureSolution datasourceStructureSolution;
|
||||||
private final EnvironmentPermission environmentPermission;
|
private final EnvironmentPermission environmentPermission;
|
||||||
private final JsonSchemaMigration jsonSchemaMigration;
|
private final JsonSchemaMigration jsonSchemaMigration;
|
||||||
|
private final FeatureFlagService featureFlagService;
|
||||||
|
|
||||||
private static final String FILE_PATH = "CRUD-DB-Table-Template-Application.json";
|
private static final String FILE_PATH = "CRUD-DB-Table-Template-Application.json";
|
||||||
|
|
||||||
|
|
@ -441,30 +444,43 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
|
||||||
String tableNameInAction = tuple.getT5();
|
String tableNameInAction = tuple.getT5();
|
||||||
String savedPageId = tuple.getT6();
|
String savedPageId = tuple.getT6();
|
||||||
log.debug("Going to clone actions from template application for page {}", savedPageId);
|
log.debug("Going to clone actions from template application for page {}", savedPageId);
|
||||||
return cloneActionsFromTemplateApplication(
|
return featureFlagService
|
||||||
datasourceStorage,
|
.check(FeatureFlagEnum.release_reactive_actions_enabled)
|
||||||
tableNameInAction,
|
.flatMap(isEnabled -> {
|
||||||
savedPageId,
|
RunBehaviourEnum runBehaviour = (isEnabled != null && isEnabled)
|
||||||
templateActionList,
|
? RunBehaviourEnum.AUTOMATIC
|
||||||
mappedColumnsAndTableName,
|
: RunBehaviourEnum.ON_PAGE_LOAD;
|
||||||
deletedWidgets,
|
return cloneActionsFromTemplateApplication(
|
||||||
pluginSpecificParams,
|
datasourceStorage,
|
||||||
templateAutogeneratedKey.toString())
|
tableNameInAction,
|
||||||
.flatMap(actionDTO -> StringUtils.equals(actionDTO.getName(), SELECT_QUERY)
|
savedPageId,
|
||||||
|| StringUtils.equals(actionDTO.getName(), FIND_QUERY)
|
templateActionList,
|
||||||
|| StringUtils.equals(actionDTO.getName(), LIST_QUERY)
|
mappedColumnsAndTableName,
|
||||||
? layoutActionService.setRunBehaviour(
|
deletedWidgets,
|
||||||
actionDTO.getId(), RunBehaviourEnum.ON_PAGE_LOAD)
|
pluginSpecificParams,
|
||||||
: Mono.just(actionDTO))
|
templateAutogeneratedKey.toString())
|
||||||
.then(applicationPageService
|
.flatMap(actionDTO -> {
|
||||||
.getPage(savedPageId, false)
|
if (StringUtils.equals(actionDTO.getName(), SELECT_QUERY)
|
||||||
.flatMap(pageDTO -> {
|
|| StringUtils.equals(actionDTO.getName(), FIND_QUERY)
|
||||||
CRUDPageResponseDTO crudPage = new CRUDPageResponseDTO();
|
|| StringUtils.equals(actionDTO.getName(), LIST_QUERY)) {
|
||||||
crudPage.setPage(pageDTO);
|
// Use the previously fetched flag value
|
||||||
createSuccessMessageAndSetAsset(plugin, crudPage);
|
// This preserves the contract and documents the intention
|
||||||
return sendGenerateCRUDPageAnalyticsEvent(
|
return layoutActionService.setRunBehaviour(
|
||||||
crudPage, datasourceStorage, plugin.getName());
|
actionDTO.getId(), runBehaviour);
|
||||||
}));
|
} else {
|
||||||
|
return Mono.just(actionDTO);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(applicationPageService
|
||||||
|
.getPage(savedPageId, false)
|
||||||
|
.flatMap(pageDTO -> {
|
||||||
|
CRUDPageResponseDTO crudPage = new CRUDPageResponseDTO();
|
||||||
|
crudPage.setPage(pageDTO);
|
||||||
|
createSuccessMessageAndSetAsset(plugin, crudPage);
|
||||||
|
return sendGenerateCRUDPageAnalyticsEvent(
|
||||||
|
crudPage, datasourceStorage, plugin.getName());
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.appsmith.server.solutions;
|
package com.appsmith.server.solutions;
|
||||||
|
|
||||||
|
import com.appsmith.external.enums.FeatureFlagEnum;
|
||||||
import com.appsmith.external.git.constants.ce.RefType;
|
import com.appsmith.external.git.constants.ce.RefType;
|
||||||
import com.appsmith.external.models.ActionConfiguration;
|
import com.appsmith.external.models.ActionConfiguration;
|
||||||
import com.appsmith.external.models.ActionDTO;
|
import com.appsmith.external.models.ActionDTO;
|
||||||
|
|
@ -40,6 +41,7 @@ import com.appsmith.server.newpages.base.NewPageService;
|
||||||
import com.appsmith.server.repositories.PluginRepository;
|
import com.appsmith.server.repositories.PluginRepository;
|
||||||
import com.appsmith.server.services.ApplicationPageService;
|
import com.appsmith.server.services.ApplicationPageService;
|
||||||
import com.appsmith.server.services.DatasourceStructureService;
|
import com.appsmith.server.services.DatasourceStructureService;
|
||||||
|
import com.appsmith.server.services.FeatureFlagService;
|
||||||
import com.appsmith.server.services.WorkspaceService;
|
import com.appsmith.server.services.WorkspaceService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
@ -50,6 +52,7 @@ import org.mockito.Mockito;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||||
import org.springframework.security.test.context.support.WithUserDetails;
|
import org.springframework.security.test.context.support.WithUserDetails;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
@ -67,6 +70,7 @@ import static com.appsmith.server.constants.ArtifactType.APPLICATION;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
|
@ -176,6 +180,9 @@ public class CreateDBTablePageSolutionTests {
|
||||||
|
|
||||||
private final CRUDPageResourceDTO resource = new CRUDPageResourceDTO();
|
private final CRUDPageResourceDTO resource = new CRUDPageResourceDTO();
|
||||||
|
|
||||||
|
@SpyBean
|
||||||
|
private FeatureFlagService featureFlagService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
|
|
||||||
|
|
@ -694,6 +701,132 @@ public class CreateDBTablePageSolutionTests {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails(value = "api_user")
|
||||||
|
public void createPageWithValidPageIdForMySqlDS_FeatureFlagEnabled() {
|
||||||
|
doReturn(Mono.just(true)).when(featureFlagService).check(FeatureFlagEnum.release_reactive_actions_enabled);
|
||||||
|
|
||||||
|
resource.setApplicationId(testApp.getId());
|
||||||
|
PageDTO newPage = new PageDTO();
|
||||||
|
newPage.setApplicationId(testApp.getId());
|
||||||
|
newPage.setName("crud-admin-page-mysql-ff-enabled");
|
||||||
|
StringBuilder pluginName = new StringBuilder();
|
||||||
|
|
||||||
|
Mono<Datasource> datasourceMono = pluginRepository.findByName("MySQL").flatMap(plugin -> {
|
||||||
|
pluginName.append(plugin.getName());
|
||||||
|
Datasource datasource = new Datasource();
|
||||||
|
datasource.setPluginId(plugin.getId());
|
||||||
|
datasource.setWorkspaceId(testWorkspace.getId());
|
||||||
|
datasource.setName("MySql-CRUD-Page-Table-DS-FF-Enabled");
|
||||||
|
|
||||||
|
HashMap<String, DatasourceStorageDTO> storages = new HashMap<>();
|
||||||
|
storages.put(
|
||||||
|
testDefaultEnvironmentId,
|
||||||
|
new DatasourceStorageDTO(null, testDefaultEnvironmentId, datasourceConfiguration));
|
||||||
|
datasource.setDatasourceStorages(storages);
|
||||||
|
|
||||||
|
return datasourceService.create(datasource).flatMap(datasource1 -> {
|
||||||
|
DatasourceStorageStructure datasourceStorageStructure = new DatasourceStorageStructure();
|
||||||
|
datasourceStorageStructure.setDatasourceId(datasource1.getId());
|
||||||
|
datasourceStorageStructure.setEnvironmentId(testDefaultEnvironmentId);
|
||||||
|
datasourceStorageStructure.setStructure(structure);
|
||||||
|
|
||||||
|
return datasourceStructureService
|
||||||
|
.save(datasourceStorageStructure)
|
||||||
|
.thenReturn(datasource1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Mono<CRUDPageResponseDTO> resultMono = datasourceMono
|
||||||
|
.flatMap(datasource1 -> {
|
||||||
|
resource.setDatasourceId(datasource1.getId());
|
||||||
|
return applicationPageService.createPage(newPage);
|
||||||
|
})
|
||||||
|
.flatMap(savedPage ->
|
||||||
|
solution.createPageFromDBTable(savedPage.getId(), resource, testDefaultEnvironmentId));
|
||||||
|
|
||||||
|
StepVerifier.create(resultMono.zipWhen(crudPageResponseDTO ->
|
||||||
|
getActions(crudPageResponseDTO.getPage().getId())))
|
||||||
|
.assertNext(tuple -> {
|
||||||
|
CRUDPageResponseDTO crudPageResponseDTO = tuple.getT1();
|
||||||
|
List<NewAction> actions = tuple.getT2();
|
||||||
|
for (NewAction action : actions) {
|
||||||
|
String name = action.getUnpublishedAction().getName();
|
||||||
|
if (SELECT_QUERY.equals(name) || FIND_QUERY.equals(name) || LIST_QUERY.equals(name)) {
|
||||||
|
assertThat(action.getUnpublishedAction().getRunBehaviour())
|
||||||
|
.isEqualTo(RunBehaviourEnum.AUTOMATIC);
|
||||||
|
} else {
|
||||||
|
assertThat(action.getUnpublishedAction().getRunBehaviour())
|
||||||
|
.isEqualTo(RunBehaviourEnum.MANUAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails(value = "api_user")
|
||||||
|
public void createPageWithValidPageIdForMySqlDS_FeatureFlagDisabled() {
|
||||||
|
doReturn(Mono.just(false)).when(featureFlagService).check(FeatureFlagEnum.release_reactive_actions_enabled);
|
||||||
|
|
||||||
|
resource.setApplicationId(testApp.getId());
|
||||||
|
PageDTO newPage = new PageDTO();
|
||||||
|
newPage.setApplicationId(testApp.getId());
|
||||||
|
newPage.setName("crud-admin-page-mysql-ff-disabled");
|
||||||
|
StringBuilder pluginName = new StringBuilder();
|
||||||
|
|
||||||
|
Mono<Datasource> datasourceMono = pluginRepository.findByName("MySQL").flatMap(plugin -> {
|
||||||
|
pluginName.append(plugin.getName());
|
||||||
|
Datasource datasource = new Datasource();
|
||||||
|
datasource.setPluginId(plugin.getId());
|
||||||
|
datasource.setWorkspaceId(testWorkspace.getId());
|
||||||
|
datasource.setName("MySql-CRUD-Page-Table-DS-FF-Disabled");
|
||||||
|
|
||||||
|
HashMap<String, DatasourceStorageDTO> storages = new HashMap<>();
|
||||||
|
storages.put(
|
||||||
|
testDefaultEnvironmentId,
|
||||||
|
new DatasourceStorageDTO(null, testDefaultEnvironmentId, datasourceConfiguration));
|
||||||
|
datasource.setDatasourceStorages(storages);
|
||||||
|
|
||||||
|
return datasourceService.create(datasource).flatMap(datasource1 -> {
|
||||||
|
DatasourceStorageStructure datasourceStorageStructure = new DatasourceStorageStructure();
|
||||||
|
datasourceStorageStructure.setDatasourceId(datasource1.getId());
|
||||||
|
datasourceStorageStructure.setEnvironmentId(testDefaultEnvironmentId);
|
||||||
|
datasourceStorageStructure.setStructure(structure);
|
||||||
|
|
||||||
|
return datasourceStructureService
|
||||||
|
.save(datasourceStorageStructure)
|
||||||
|
.thenReturn(datasource1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Mono<CRUDPageResponseDTO> resultMono = datasourceMono
|
||||||
|
.flatMap(datasource1 -> {
|
||||||
|
resource.setDatasourceId(datasource1.getId());
|
||||||
|
return applicationPageService.createPage(newPage);
|
||||||
|
})
|
||||||
|
.flatMap(savedPage ->
|
||||||
|
solution.createPageFromDBTable(savedPage.getId(), resource, testDefaultEnvironmentId));
|
||||||
|
|
||||||
|
StepVerifier.create(resultMono.zipWhen(crudPageResponseDTO ->
|
||||||
|
getActions(crudPageResponseDTO.getPage().getId())))
|
||||||
|
.assertNext(tuple -> {
|
||||||
|
CRUDPageResponseDTO crudPageResponseDTO = tuple.getT1();
|
||||||
|
List<NewAction> actions = tuple.getT2();
|
||||||
|
for (NewAction action : actions) {
|
||||||
|
String name = action.getUnpublishedAction().getName();
|
||||||
|
if (SELECT_QUERY.equals(name) || FIND_QUERY.equals(name) || LIST_QUERY.equals(name)) {
|
||||||
|
assertThat(action.getUnpublishedAction().getRunBehaviour())
|
||||||
|
.isEqualTo(RunBehaviourEnum.ON_PAGE_LOAD);
|
||||||
|
} else {
|
||||||
|
assertThat(action.getUnpublishedAction().getRunBehaviour())
|
||||||
|
.isEqualTo(RunBehaviourEnum.MANUAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithUserDetails(value = "api_user")
|
@WithUserDetails(value = "api_user")
|
||||||
public void createPageWithValidPageIdForRedshiftDS() {
|
public void createPageWithValidPageIdForRedshiftDS() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user