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.services.AnalyticsService;
|
||||
import com.appsmith.server.services.ApplicationPageService;
|
||||
import com.appsmith.server.services.FeatureFlagService;
|
||||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.solutions.ce.CreateDBTablePageSolutionCEImpl;
|
||||
|
|
@ -37,7 +38,8 @@ public class CreateDBTablePageSolutionImpl extends CreateDBTablePageSolutionCEIm
|
|||
PagePermission pagePermission,
|
||||
DatasourceStructureSolution datasourceStructureSolution,
|
||||
EnvironmentPermission environmentPermission,
|
||||
JsonSchemaMigration jsonSchemaMigration) {
|
||||
JsonSchemaMigration jsonSchemaMigration,
|
||||
FeatureFlagService featureFlagService) {
|
||||
super(
|
||||
datasourceService,
|
||||
datasourceStorageService,
|
||||
|
|
@ -55,6 +57,7 @@ public class CreateDBTablePageSolutionImpl extends CreateDBTablePageSolutionCEIm
|
|||
pagePermission,
|
||||
datasourceStructureSolution,
|
||||
environmentPermission,
|
||||
jsonSchemaMigration);
|
||||
jsonSchemaMigration,
|
||||
featureFlagService);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.ActionCreationSourceTypeEnum;
|
|||
import com.appsmith.external.constants.AnalyticsEvents;
|
||||
import com.appsmith.external.converters.HttpMethodConverter;
|
||||
import com.appsmith.external.converters.ISOStringToInstantConverter;
|
||||
import com.appsmith.external.enums.FeatureFlagEnum;
|
||||
import com.appsmith.external.git.constants.ce.RefType;
|
||||
import com.appsmith.external.helpers.AppsmithBeanUtils;
|
||||
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.services.AnalyticsService;
|
||||
import com.appsmith.server.services.ApplicationPageService;
|
||||
import com.appsmith.server.services.FeatureFlagService;
|
||||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.solutions.ApplicationPermission;
|
||||
|
|
@ -96,6 +98,7 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
|
|||
private final DatasourceStructureSolution datasourceStructureSolution;
|
||||
private final EnvironmentPermission environmentPermission;
|
||||
private final JsonSchemaMigration jsonSchemaMigration;
|
||||
private final FeatureFlagService featureFlagService;
|
||||
|
||||
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 savedPageId = tuple.getT6();
|
||||
log.debug("Going to clone actions from template application for page {}", savedPageId);
|
||||
return cloneActionsFromTemplateApplication(
|
||||
datasourceStorage,
|
||||
tableNameInAction,
|
||||
savedPageId,
|
||||
templateActionList,
|
||||
mappedColumnsAndTableName,
|
||||
deletedWidgets,
|
||||
pluginSpecificParams,
|
||||
templateAutogeneratedKey.toString())
|
||||
.flatMap(actionDTO -> StringUtils.equals(actionDTO.getName(), SELECT_QUERY)
|
||||
|| StringUtils.equals(actionDTO.getName(), FIND_QUERY)
|
||||
|| StringUtils.equals(actionDTO.getName(), LIST_QUERY)
|
||||
? layoutActionService.setRunBehaviour(
|
||||
actionDTO.getId(), RunBehaviourEnum.ON_PAGE_LOAD)
|
||||
: 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());
|
||||
}));
|
||||
return featureFlagService
|
||||
.check(FeatureFlagEnum.release_reactive_actions_enabled)
|
||||
.flatMap(isEnabled -> {
|
||||
RunBehaviourEnum runBehaviour = (isEnabled != null && isEnabled)
|
||||
? RunBehaviourEnum.AUTOMATIC
|
||||
: RunBehaviourEnum.ON_PAGE_LOAD;
|
||||
return cloneActionsFromTemplateApplication(
|
||||
datasourceStorage,
|
||||
tableNameInAction,
|
||||
savedPageId,
|
||||
templateActionList,
|
||||
mappedColumnsAndTableName,
|
||||
deletedWidgets,
|
||||
pluginSpecificParams,
|
||||
templateAutogeneratedKey.toString())
|
||||
.flatMap(actionDTO -> {
|
||||
if (StringUtils.equals(actionDTO.getName(), SELECT_QUERY)
|
||||
|| StringUtils.equals(actionDTO.getName(), FIND_QUERY)
|
||||
|| StringUtils.equals(actionDTO.getName(), LIST_QUERY)) {
|
||||
// Use the previously fetched flag value
|
||||
// This preserves the contract and documents the intention
|
||||
return layoutActionService.setRunBehaviour(
|
||||
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;
|
||||
|
||||
import com.appsmith.external.enums.FeatureFlagEnum;
|
||||
import com.appsmith.external.git.constants.ce.RefType;
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
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.services.ApplicationPageService;
|
||||
import com.appsmith.server.services.DatasourceStructureService;
|
||||
import com.appsmith.server.services.FeatureFlagService;
|
||||
import com.appsmith.server.services.WorkspaceService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
|
@ -50,6 +52,7 @@ import org.mockito.Mockito;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
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.test.annotation.DirtiesContext;
|
||||
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.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
@Slf4j
|
||||
|
|
@ -176,6 +180,9 @@ public class CreateDBTablePageSolutionTests {
|
|||
|
||||
private final CRUDPageResourceDTO resource = new CRUDPageResourceDTO();
|
||||
|
||||
@SpyBean
|
||||
private FeatureFlagService featureFlagService;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
|
||||
|
|
@ -694,6 +701,132 @@ public class CreateDBTablePageSolutionTests {
|
|||
.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
|
||||
@WithUserDetails(value = "api_user")
|
||||
public void createPageWithValidPageIdForRedshiftDS() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user