chore: refactor to use layout service for onPageLoad actions calculations in blocks API (#34269)

## Description
Refactor the consolidated API for importing blocks


Fixes 
## Automation

/ok-to-test tags="@tag.Templates"

### 🔍 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/9690978421>
> Commit: ce3884a3db7b99cd36b0d600b43120d8eafb61fb
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9690978421&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Templates`

<!-- 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

- **Refactor**
- Improved code structure by extending interface and class compatibility
for `ConsolidatedAPIService` and `ConsolidatedAPIServiceImpl`.

- **New Features**
- Enhanced layout management by importing `UpdateLayoutService` and
updating related logic to improve page load actions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Abhijeet <41686026+abhvsn@users.noreply.github.com>
This commit is contained in:
Anagh Hegde 2024-06-27 14:08:35 +05:30 committed by GitHub
parent 2f12030a9a
commit c3047e8e57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 169 additions and 113 deletions

View File

@ -1,10 +1,9 @@
package com.appsmith.server.dtos;
import com.appsmith.external.dtos.DslExecutableDTO;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.Datasource;
import com.appsmith.server.domains.ActionCollection;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.domains.NewAction;
import lombok.Data;
import java.util.List;
@ -16,10 +15,7 @@ public class BuildingBlockResponseDTO {
List<DslExecutableDTO> onPageLoadActions;
// New actions created in the current flow
List<NewAction> newActionList;
// New actionCollection created in the current flow
List<ActionCollection> actionCollectionList;
List<ActionDTO> newActionList;
// All datasource in the workspace
List<Datasource> datasourceList;

View File

@ -2,6 +2,7 @@ package com.appsmith.server.imports.internal.partial;
import com.appsmith.external.constants.AnalyticsEvents;
import com.appsmith.external.helpers.Stopwatch;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.CreatorContextType;
import com.appsmith.external.models.Datasource;
import com.appsmith.server.acl.AclPermission;
@ -30,6 +31,7 @@ import com.appsmith.server.helpers.ImportArtifactPermissionProvider;
import com.appsmith.server.imports.importable.ImportableService;
import com.appsmith.server.imports.internal.ImportService;
import com.appsmith.server.jslibs.base.CustomJSLibService;
import com.appsmith.server.layouts.UpdateLayoutService;
import com.appsmith.server.newactions.base.NewActionService;
import com.appsmith.server.newpages.base.NewPageService;
import com.appsmith.server.refactors.applications.RefactoringService;
@ -50,6 +52,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.codec.multipart.Part;
import org.springframework.transaction.reactive.TransactionalOperator;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -58,10 +62,11 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.appsmith.server.constants.ce.FieldNameCE.WORKSPACE_ID;
@RequiredArgsConstructor
@Slf4j
public class PartialImportServiceCEImpl implements PartialImportServiceCE {
@ -93,6 +98,7 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE {
private final ActionCollectionService actionCollectionService;
private final DatasourceService datasourceService;
private final CustomJSLibService customJSLibService;
private final UpdateLayoutService updateLayoutService;
@Override
public Mono<Application> importResourceInPage(
@ -422,6 +428,9 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE {
Mono<ApplicationJson> applicationJsonMono =
applicationTemplateService.getApplicationJsonFromTemplate(buildingBlockDTO.getTemplateId());
Mono<String> branchedPageIdMono =
newPageService.findBranchedPageId(branchName, buildingBlockDTO.getPageId(), AclPermission.MANAGE_PAGES);
Stopwatch processStopwatch = new Stopwatch("Download Content from Cloud service");
return applicationJsonMono.flatMap(applicationJson -> {
processStopwatch.stopAndLogTimeInMillis();
@ -432,7 +441,10 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE {
buildingBlockDTO.getPageId(),
branchName,
applicationJson)
.flatMap(buildingBlockImportDTO -> {
.zipWith(branchedPageIdMono)
.flatMap(tuple -> {
BuildingBlockImportDTO buildingBlockImportDTO = tuple.getT1();
String branchedPageId = tuple.getT2();
processStopwatch1.stopAndLogTimeInMillis();
// Fetch layout and get new onPageLoadActions
// This data is not present in a client, since these are created
@ -441,117 +453,77 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE {
buildingBlockResponseDTO.setWidgetDsl(buildingBlockImportDTO.getWidgetDsl());
buildingBlockResponseDTO.setOnPageLoadActions(new ArrayList<>());
Set<String> newOnPageLoadActionNames = new HashSet<>();
applicationJson
.getPageList()
.get(0)
.getPublishedPage()
.getLayouts()
.get(0)
.getLayoutOnLoadActions()
.forEach(dslExecutableDTOS -> {
dslExecutableDTOS.forEach(dslExecutableDTO -> {
if (dslExecutableDTO.getName() != null) {
// Use the refactored names to get the correct ids
if (buildingBlockImportDTO
.getRefactoredEntityNameMap()
.get(dslExecutableDTO.getName())
!= null) {
dslExecutableDTO.setName(buildingBlockImportDTO
.getRefactoredEntityNameMap()
.get(dslExecutableDTO.getName()));
}
newOnPageLoadActionNames.add(
dslExecutableDTO.getName().contains(".")
? dslExecutableDTO.getName()
.split("\\.")[0]
: dslExecutableDTO.getName());
buildingBlockResponseDTO
.getOnPageLoadActions()
.add(dslExecutableDTO);
}
});
});
// Fetch the datasource and customJsLibs
Mono<List<Datasource>> datasourceList = datasourceService
.getAllByWorkspaceIdWithStorages(
buildingBlockDTO.getWorkspaceId(), AclPermission.MANAGE_DATASOURCES)
.collectList();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add(WORKSPACE_ID, buildingBlockDTO.getWorkspaceId());
Mono<List<Datasource>> datasourceList =
datasourceService.getAllWithStorages(params).collectList();
Mono<List<CustomJSLib>> customJSLibs = customJSLibService.getAllJSLibsInContext(
buildingBlockDTO.getApplicationId(), CreatorContextType.APPLICATION, branchName, false);
// Fetch all actions and action collections and update the onPageLoadActions with correct ids
return Mono.zip(
actionCollectionService
.findByPageId(buildingBlockDTO.getPageId())
.collectList(),
newActionService
.findByPageId(buildingBlockDTO.getPageId())
.collectList(),
datasourceList,
customJSLibs)
.flatMap(tuple -> {
List<ActionCollection> actionCollections = tuple.getT1();
List<NewAction> newActions = tuple.getT2();
Mono<List<ActionDTO>> actionList = newActionService
.getUnpublishedActionsByPageId(branchedPageId, AclPermission.MANAGE_ACTIONS)
.collectList();
buildingBlockResponseDTO.setDatasourceList(tuple.getT3());
buildingBlockResponseDTO.setCustomJSLibList(tuple.getT4());
// Filter the newly created actions and actionCollection
buildingBlockResponseDTO.setNewActionList(newActions.stream()
.filter(newAction -> buildingBlockImportDTO
.getRefactoredEntityNameMap()
.containsValue(newAction
.getUnpublishedAction()
.getName()))
.toList());
buildingBlockResponseDTO.setActionCollectionList(actionCollections.stream()
.filter(actionCollection -> buildingBlockImportDTO
.getRefactoredEntityNameMap()
.containsValue(actionCollection
.getUnpublishedCollection()
.getName()))
.toList());
MultiValueMap<String, String> params1 = new LinkedMultiValueMap<>();
params1.add(FieldName.PAGE_ID, branchedPageId);
newActionService.getUnpublishedActions(params1, branchName);
actionCollections.forEach(actionCollection -> {
if (newOnPageLoadActionNames.contains(actionCollection
.getUnpublishedCollection()
.getName())) {
buildingBlockResponseDTO
.getOnPageLoadActions()
.forEach(dslExecutableDTO -> {
if (dslExecutableDTO.getCollectionId() != null) {
String name = dslExecutableDTO.getCollectionId()
.split("_")[1];
if (name.equals(actionCollection
.getUnpublishedCollection()
.getName())) {
dslExecutableDTO.setId(actionCollection.getId());
dslExecutableDTO.setCollectionId(
actionCollection.getId());
}
}
});
}
});
List<String> newActionNames = applicationJson.getActionList().stream()
.map(newAction ->
newAction.getUnpublishedAction().getName())
.toList();
newActions.forEach(newAction -> {
if (newOnPageLoadActionNames.contains(
newAction.getUnpublishedAction().getName())) {
buildingBlockResponseDTO
.getOnPageLoadActions()
.forEach(dslExecutableDTO -> {
if (dslExecutableDTO
.getName()
.equals(newAction
.getUnpublishedAction()
.getName())) {
dslExecutableDTO.setId(newAction.getId());
}
});
}
});
return newPageService
.findByBranchNameAndDefaultPageId(
branchName, buildingBlockDTO.getPageId(), AclPermission.MANAGE_PAGES)
.flatMap(newPage -> {
String layoutId = newPage.getUnpublishedPage()
.getLayouts()
.get(0)
.getId();
Layout layout = applicationJson
.getPageList()
.get(0)
.getUnpublishedPage()
.getLayouts()
.get(0);
layout.setDsl(widgetRefactorUtil.convertDslStringToJSONObject(
buildingBlockImportDTO.getWidgetDsl()));
// fetch the layout and get the onPageLoadActions
return updateLayoutService
.getOnPageLoadActions(
buildingBlockDTO.getPageId(),
layoutId,
layout,
buildingBlockImportDTO
.getApplication()
.getEvaluationVersion(),
CreatorContextType.PAGE)
.flatMap(layoutDTO -> {
layoutDTO.forEach(actionSet -> {
buildingBlockResponseDTO
.getOnPageLoadActions()
.addAll(actionSet.stream()
.toList());
});
return Mono.just(buildingBlockResponseDTO);
return Mono.zip(datasourceList, customJSLibs, actionList);
})
.map(tuple3 -> {
buildingBlockResponseDTO.setDatasourceList(tuple3.getT1());
buildingBlockResponseDTO.setCustomJSLibList(tuple3.getT2());
buildingBlockResponseDTO.setNewActionList(tuple3.getT3());
return buildingBlockResponseDTO;
});
})
// Remove the existing actions in the page from the newActions list
.flatMap(buildingBlockResponseDTO1 -> {
buildingBlockResponseDTO1
.getNewActionList()
.removeIf(actionDTO -> !newActionNames.contains(actionDTO.getName()));
return Mono.just(buildingBlockResponseDTO1);
});
});
});

View File

@ -12,6 +12,7 @@ import com.appsmith.server.domains.Plugin;
import com.appsmith.server.imports.importable.ImportableService;
import com.appsmith.server.imports.internal.ImportService;
import com.appsmith.server.jslibs.base.CustomJSLibService;
import com.appsmith.server.layouts.UpdateLayoutService;
import com.appsmith.server.newactions.base.NewActionService;
import com.appsmith.server.newpages.base.NewPageService;
import com.appsmith.server.refactors.applications.RefactoringService;
@ -64,7 +65,8 @@ public class PartialImportServiceImpl extends PartialImportServiceCEImpl impleme
NewActionService newActionService,
ActionCollectionService actionCollectionService,
DatasourceService datasourceService,
CustomJSLibService customJSLibService) {
CustomJSLibService customJSLibService,
UpdateLayoutService updateLayoutService) {
super(
importService,
workspaceService,
@ -92,6 +94,7 @@ public class PartialImportServiceImpl extends PartialImportServiceCEImpl impleme
newActionService,
actionCollectionService,
datasourceService,
customJSLibService);
customJSLibService,
updateLayoutService);
}
}

View File

@ -1,11 +1,16 @@
package com.appsmith.server.layouts;
import com.appsmith.external.dtos.DslExecutableDTO;
import com.appsmith.external.models.CreatorContextType;
import com.appsmith.server.domains.Layout;
import com.appsmith.server.dtos.LayoutDTO;
import com.appsmith.server.dtos.UpdateMultiplePageLayoutDTO;
import net.minidev.json.JSONObject;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Set;
public interface UpdateLayoutServiceCE {
Mono<LayoutDTO> updateLayout(String pageId, String applicationId, String layoutId, Layout layout);
@ -18,4 +23,7 @@ public interface UpdateLayoutServiceCE {
JSONObject unescapeMongoSpecialCharacters(Layout layout);
Mono<String> updatePageLayoutsByPageId(String pageId);
Mono<List<Set<DslExecutableDTO>>> getOnPageLoadActions(
String creatorId, String layoutId, Layout layout, Integer evaluatedVersion, CreatorContextType creatorType);
}

View File

@ -304,6 +304,67 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
.then(Mono.just(pageId));
}
@Override
public Mono<List<Set<DslExecutableDTO>>> getOnPageLoadActions(
String creatorId,
String layoutId,
Layout layout,
Integer evaluatedVersion,
CreatorContextType creatorType) {
JSONObject dsl = layout.getDsl();
if (dsl == null) {
// There is no DSL here. No need to process anything. Return as is.
return Mono.just(new ArrayList<>());
}
Set<String> widgetNames = new HashSet<>();
Map<String, Set<String>> widgetDynamicBindingsMap = new HashMap<>();
Set<String> escapedWidgetNames = new HashSet<>();
try {
dsl = extractAllWidgetNamesAndDynamicBindingsFromDSL(
dsl, widgetNames, widgetDynamicBindingsMap, creatorId, layoutId, escapedWidgetNames, creatorType);
} catch (Throwable t) {
return sendUpdateLayoutAnalyticsEvent(creatorId, layoutId, dsl, false, t, creatorType)
.then(Mono.error(t));
}
layout.setWidgetNames(widgetNames);
if (!escapedWidgetNames.isEmpty()) {
layout.setMongoEscapedWidgetNames(escapedWidgetNames);
}
Set<ExecutableDependencyEdge> edges = new HashSet<>();
Set<String> executablesUsedInDSL = new HashSet<>();
List<Executable> flatmapOnLoadExecutables = new ArrayList<>();
AtomicReference<Boolean> validOnLoadExecutables = new AtomicReference<>(Boolean.TRUE);
// setting the layoutOnLoadActionActionErrors to empty to remove the existing errors before new DAG calculation.
layout.setLayoutOnLoadActionErrors(new ArrayList<>());
return onLoadExecutablesUtil
.findAllOnLoadExecutables(
creatorId,
evaluatedVersion,
widgetNames,
edges,
widgetDynamicBindingsMap,
flatmapOnLoadExecutables,
executablesUsedInDSL,
creatorType)
.onErrorResume(AppsmithException.class, error -> {
log.info(error.getMessage());
validOnLoadExecutables.set(FALSE);
layout.setLayoutOnLoadActionErrors(List.of(new ErrorDTO(
error.getAppErrorCode(),
error.getErrorType(),
layoutOnLoadActionErrorToastMessage,
error.getMessage(),
error.getTitle())));
return Mono.just(new ArrayList<>());
});
}
private JSONObject unEscapeDslKeys(JSONObject dsl, Set<String> escapedWidgetNames) {
String widgetName = (String) dsl.get(FieldName.WIDGET_NAME);

View File

@ -2,6 +2,8 @@ package com.appsmith.server.widgets.refactors;
import com.appsmith.external.models.MustacheBindingToken;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.DslUtils;
import com.appsmith.server.services.AstService;
import com.fasterxml.jackson.databind.JsonNode;
@ -10,7 +12,10 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@ -24,6 +29,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@Slf4j
@Component
@RequiredArgsConstructor
public class WidgetRefactorUtil {
@ -197,4 +203,14 @@ public class WidgetRefactorUtil {
public JsonNode convertDslStringToJsonNode(JSONObject dsl) {
return objectMapper.convertValue(dsl, JsonNode.class);
}
public JSONObject convertDslStringToJSONObject(String dsl) {
JSONParser jsonParser = new JSONParser();
try {
return (JSONObject) jsonParser.parse(dsl);
} catch (ParseException exception) {
log.error("Error parsing the page dsl for page: {}", exception.getMessage());
throw new AppsmithException(AppsmithError.JSON_PROCESSING_ERROR);
}
}
}