Move action also works successfully in removing the action from the previous page's onLoadActions while moving to another page.
This commit is contained in:
parent
a542abc563
commit
28e20ed3bd
|
|
@ -3,11 +3,13 @@ package com.appsmith.server.controllers;
|
||||||
import com.appsmith.external.models.ActionExecutionResult;
|
import com.appsmith.external.models.ActionExecutionResult;
|
||||||
import com.appsmith.server.constants.Url;
|
import com.appsmith.server.constants.Url;
|
||||||
import com.appsmith.server.domains.Action;
|
import com.appsmith.server.domains.Action;
|
||||||
|
import com.appsmith.server.dtos.ActionMoveDTO;
|
||||||
import com.appsmith.server.dtos.ExecuteActionDTO;
|
import com.appsmith.server.dtos.ExecuteActionDTO;
|
||||||
import com.appsmith.server.dtos.ResponseDTO;
|
import com.appsmith.server.dtos.ResponseDTO;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.services.ActionCollectionService;
|
import com.appsmith.server.services.ActionCollectionService;
|
||||||
import com.appsmith.server.services.ActionService;
|
import com.appsmith.server.services.ActionService;
|
||||||
|
import com.appsmith.server.services.LayoutActionService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
@ -28,12 +30,15 @@ import javax.validation.Valid;
|
||||||
public class ActionController extends BaseController<ActionService, Action, String> {
|
public class ActionController extends BaseController<ActionService, Action, String> {
|
||||||
|
|
||||||
private final ActionCollectionService actionCollectionService;
|
private final ActionCollectionService actionCollectionService;
|
||||||
|
private final LayoutActionService layoutActionService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ActionController(ActionService service,
|
public ActionController(ActionService service,
|
||||||
ActionCollectionService actionCollectionService) {
|
ActionCollectionService actionCollectionService,
|
||||||
|
LayoutActionService layoutActionService) {
|
||||||
super(service);
|
super(service);
|
||||||
this.actionCollectionService = actionCollectionService;
|
this.actionCollectionService = actionCollectionService;
|
||||||
|
this.layoutActionService = layoutActionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|
@ -56,4 +61,11 @@ public class ActionController extends BaseController<ActionService, Action, Stri
|
||||||
return service.executeAction(executeActionDTO)
|
return service.executeAction(executeActionDTO)
|
||||||
.map(updatedResource -> new ResponseDTO<>(HttpStatus.OK.value(), updatedResource, null));
|
.map(updatedResource -> new ResponseDTO<>(HttpStatus.OK.value(), updatedResource, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/move")
|
||||||
|
public Mono<ResponseDTO<Action>> moveAction(@RequestBody @Valid ActionMoveDTO actionMoveDTO) {
|
||||||
|
log.debug("Going to move action {} from page {} to page {}", actionMoveDTO.getAction().getName(), actionMoveDTO.getAction().getPageId(), actionMoveDTO.getDestinationPageId());
|
||||||
|
return layoutActionService.moveAction(actionMoveDTO)
|
||||||
|
.map(action -> new ResponseDTO<>(HttpStatus.OK.value(), action, null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.appsmith.server.controllers;
|
||||||
import com.appsmith.server.constants.Url;
|
import com.appsmith.server.constants.Url;
|
||||||
import com.appsmith.server.domains.Layout;
|
import com.appsmith.server.domains.Layout;
|
||||||
import com.appsmith.server.dtos.ResponseDTO;
|
import com.appsmith.server.dtos.ResponseDTO;
|
||||||
|
import com.appsmith.server.services.LayoutActionService;
|
||||||
import com.appsmith.server.services.LayoutService;
|
import com.appsmith.server.services.LayoutService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
@ -22,10 +23,13 @@ import javax.validation.Valid;
|
||||||
public class LayoutController {
|
public class LayoutController {
|
||||||
|
|
||||||
private final LayoutService service;
|
private final LayoutService service;
|
||||||
|
private final LayoutActionService layoutActionService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LayoutController(LayoutService layoutService) {
|
public LayoutController(LayoutService layoutService,
|
||||||
|
LayoutActionService layoutActionService) {
|
||||||
this.service = layoutService;
|
this.service = layoutService;
|
||||||
|
this.layoutActionService = layoutActionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/pages/{pageId}")
|
@PostMapping("/pages/{pageId}")
|
||||||
|
|
@ -42,7 +46,7 @@ public class LayoutController {
|
||||||
|
|
||||||
@PutMapping("/{layoutId}/pages/{pageId}")
|
@PutMapping("/{layoutId}/pages/{pageId}")
|
||||||
public Mono<ResponseDTO<Layout>> updateLayout(@PathVariable String pageId, @PathVariable String layoutId, @RequestBody Layout layout) {
|
public Mono<ResponseDTO<Layout>> updateLayout(@PathVariable String pageId, @PathVariable String layoutId, @RequestBody Layout layout) {
|
||||||
return service.updateLayout(pageId, layoutId, layout)
|
return layoutActionService.updateLayout(pageId, layoutId, layout)
|
||||||
.map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null));
|
.map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public class RestApiImportController {
|
||||||
ApiImporter service;
|
ApiImporter service;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CURL :
|
case CURL:
|
||||||
service = curlImporterService;
|
service = curlImporterService;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public class UserController extends BaseController<UserService, User, String> {
|
||||||
* in order to construct client facing URLs that will be sent to the user over email.
|
* in order to construct client facing URLs that will be sent to the user over email.
|
||||||
*
|
*
|
||||||
* @param userPasswordDTO
|
* @param userPasswordDTO
|
||||||
* @param originHeader The Origin header in the request. This is a mandatory parameter.
|
* @param originHeader The Origin header in the request. This is a mandatory parameter.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/forgotPassword")
|
@PostMapping("/forgotPassword")
|
||||||
|
|
@ -83,7 +83,7 @@ public class UserController extends BaseController<UserService, User, String> {
|
||||||
* This function creates an invite for a new user to join the Appsmith platform. We require the Origin header
|
* This function creates an invite for a new user to join the Appsmith platform. We require the Origin header
|
||||||
* in order to construct client facing URLs that will be sent to the user via email.
|
* in order to construct client facing URLs that will be sent to the user via email.
|
||||||
*
|
*
|
||||||
* @param user The user object for the new user being invited to the Appsmith platform
|
* @param user The user object for the new user being invited to the Appsmith platform
|
||||||
* @param originHeader Origin header in the request
|
* @param originHeader Origin header in the request
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.appsmith.server.dtos;
|
||||||
|
|
||||||
|
import com.appsmith.server.domains.Action;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ActionMoveDTO {
|
||||||
|
@NotNull
|
||||||
|
Action action;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
String destinationPageId;
|
||||||
|
}
|
||||||
|
|
@ -430,8 +430,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
|
||||||
public Flux<Action> saveAll(List<Action> actions) {
|
public Flux<Action> saveAll(List<Action> actions) {
|
||||||
return repository.saveAll(actions);
|
return repository.saveAll(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function replaces the variables in the Object with the actual params
|
* This function replaces the variables in the Object with the actual params
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class CurlImporterService extends BaseApiImporter{
|
public class CurlImporterService extends BaseApiImporter {
|
||||||
|
|
||||||
private static final String headerRegex = "\\-H\\s+\\'(.+?)\\'";
|
private static final String headerRegex = "\\-H\\s+\\'(.+?)\\'";
|
||||||
private static final String methodRegex = "\\-X\\s+(.+?)\\b";
|
private static final String methodRegex = "\\-X\\s+(.+?)\\b";
|
||||||
|
|
@ -56,21 +56,21 @@ public class CurlImporterService extends BaseApiImporter{
|
||||||
// Find all the headers here
|
// Find all the headers here
|
||||||
List<Property> headers = actionConfiguration.getHeaders();
|
List<Property> headers = actionConfiguration.getHeaders();
|
||||||
while (headerMatcher.find()) {
|
while (headerMatcher.find()) {
|
||||||
String headerString = headerMatcher.group();
|
String headerString = headerMatcher.group();
|
||||||
String[] splitHeader = headerString.split("'");
|
String[] splitHeader = headerString.split("'");
|
||||||
|
|
||||||
String header = splitHeader[1];
|
String header = splitHeader[1];
|
||||||
String[] keyValuePairInString = header.split(":");
|
String[] keyValuePairInString = header.split(":");
|
||||||
|
|
||||||
Property property = new Property();
|
Property property = new Property();
|
||||||
property.setKey(keyValuePairInString[0]);
|
property.setKey(keyValuePairInString[0]);
|
||||||
property.setValue(keyValuePairInString[1]);
|
property.setValue(keyValuePairInString[1]);
|
||||||
|
|
||||||
if (headers == null) {
|
if (headers == null) {
|
||||||
headers = new ArrayList<>();
|
headers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.add(property);
|
headers.add(property);
|
||||||
}
|
}
|
||||||
actionConfiguration.setHeaders(headers);
|
actionConfiguration.setHeaders(headers);
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ public class CurlImporterService extends BaseApiImporter{
|
||||||
Boolean urlFound = false;
|
Boolean urlFound = false;
|
||||||
// Find the URL now
|
// Find the URL now
|
||||||
//Ignoring the first word which is "curl"
|
//Ignoring the first word which is "curl"
|
||||||
for (int i = 1; i< cmdSplit.length; i++) {
|
for (int i = 1; i < cmdSplit.length; i++) {
|
||||||
try {
|
try {
|
||||||
// If the string doesnt throw an exception when being converted to a URI, its a valid URL.
|
// If the string doesnt throw an exception when being converted to a URI, its a valid URL.
|
||||||
URI uri = new URL(cmdSplit[i]).toURI();
|
URI uri = new URL(cmdSplit[i]).toURI();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
|
import com.appsmith.server.domains.Action;
|
||||||
|
import com.appsmith.server.domains.Layout;
|
||||||
|
import com.appsmith.server.dtos.ActionMoveDTO;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
public interface LayoutActionService {
|
||||||
|
public Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout);
|
||||||
|
|
||||||
|
public Mono<Action> moveAction(ActionMoveDTO actionMoveDTO);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.domains.Action;
|
||||||
|
import com.appsmith.server.domains.Layout;
|
||||||
|
import com.appsmith.server.domains.Page;
|
||||||
|
import com.appsmith.server.dtos.ActionMoveDTO;
|
||||||
|
import com.appsmith.server.dtos.DslActionDTO;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.minidev.json.JSONObject;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static com.appsmith.server.helpers.MustacheHelper.extractMustacheKeys;
|
||||||
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class LayoutActionServiceImpl implements LayoutActionService {
|
||||||
|
private final ActionService actionService;
|
||||||
|
private final PageService pageService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private final ApplicationPageService applicationPageService;
|
||||||
|
/*
|
||||||
|
* This pattern finds all the String which have been extracted from the mustache dynamic bindings.
|
||||||
|
* e.g. for the given JS function using action with name "fetchUsers"
|
||||||
|
* {{JSON.stringify(fetchUsers)}}
|
||||||
|
* This pattern should return ["JSON.stringify", "fetchUsers"]
|
||||||
|
*/
|
||||||
|
private final Pattern pattern = Pattern.compile("[a-zA-Z0-9._]+");
|
||||||
|
|
||||||
|
|
||||||
|
public LayoutActionServiceImpl(ActionService actionService,
|
||||||
|
PageService pageService,
|
||||||
|
ObjectMapper objectMapper,
|
||||||
|
ApplicationPageService applicationPageService) {
|
||||||
|
this.actionService = actionService;
|
||||||
|
this.pageService = pageService;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
this.applicationPageService = applicationPageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout) {
|
||||||
|
String dslString = "";
|
||||||
|
|
||||||
|
// Convert the DSL into a String
|
||||||
|
JSONObject dsl = layout.getDsl();
|
||||||
|
try {
|
||||||
|
dslString = objectMapper.writeValueAsString(dsl);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.debug("Exception caught during conversion of DSL Json object to String. ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mono<Set<String>> dynamicBindingNamesMono = Mono.just(dslString)
|
||||||
|
// Extract all the mustache keys in the DSL to get the dynamic bindings used in the DSL.
|
||||||
|
.map(dslString1 -> extractMustacheKeys(dslString1))
|
||||||
|
.map(dynamicBindings -> {
|
||||||
|
Set<String> dynamicBindingNames = new HashSet<>();
|
||||||
|
if (!dynamicBindings.isEmpty()) {
|
||||||
|
for (String mustacheKey : dynamicBindings) {
|
||||||
|
String key = mustacheKey.trim();
|
||||||
|
|
||||||
|
// Extract all the words in the dynamic bindings
|
||||||
|
Matcher matcher = pattern.matcher(key);
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String word = matcher.group();
|
||||||
|
|
||||||
|
String[] subStrings = word.split(Pattern.quote("."));
|
||||||
|
if (subStrings.length > 0) {
|
||||||
|
// We are only interested in the top level. e.g. if its Input1.text, we want just Input1
|
||||||
|
dynamicBindingNames.add(subStrings[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dynamicBindingNames;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Mono<Set<DslActionDTO>> onLoadActionsMono = dynamicBindingNamesMono
|
||||||
|
.flatMapMany(dynamicBindingNames -> findRestApiActionsByPageIdAndHTTPMethodGET(dynamicBindingNames, pageId))
|
||||||
|
.map(action -> {
|
||||||
|
// Since we are only interested in few fields, prepare the DslActionDTO that needs to be stored in
|
||||||
|
// the layout and return it to be collected in to a set.
|
||||||
|
DslActionDTO newAction = new DslActionDTO();
|
||||||
|
newAction.setId(action.getId());
|
||||||
|
newAction.setPluginType(action.getPluginType());
|
||||||
|
newAction.setJsonPathKeys(action.getJsonPathKeys());
|
||||||
|
newAction.setName(action.getName());
|
||||||
|
return newAction;
|
||||||
|
})
|
||||||
|
.collect(toSet());
|
||||||
|
|
||||||
|
return pageService.findByIdAndLayoutsId(pageId, layoutId)
|
||||||
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.PAGE_ID + " or " + FieldName.LAYOUT_ID)))
|
||||||
|
.zipWith(onLoadActionsMono)
|
||||||
|
.map(tuple -> {
|
||||||
|
Page page = tuple.getT1();
|
||||||
|
Set<DslActionDTO> onLoadActions = tuple.getT2();
|
||||||
|
|
||||||
|
List<Layout> layoutList = page.getLayouts();
|
||||||
|
|
||||||
|
//Because the findByIdAndLayoutsId call returned non-empty result, we are guaranteed to find the layoutId here.
|
||||||
|
for (Layout storedLayout : layoutList) {
|
||||||
|
if (storedLayout.getId().equals(layoutId)) {
|
||||||
|
//Copy the variables to conserve before update
|
||||||
|
JSONObject publishedDsl = storedLayout.getPublishedDsl();
|
||||||
|
Set<DslActionDTO> publishedLayoutOnLoadActions = storedLayout.getPublishedLayoutOnLoadActions();
|
||||||
|
|
||||||
|
//Update
|
||||||
|
layout.setLayoutOnLoadActions(onLoadActions);
|
||||||
|
BeanUtils.copyProperties(layout, storedLayout);
|
||||||
|
storedLayout.setId(layoutId);
|
||||||
|
|
||||||
|
//Copy back the conserved variables.
|
||||||
|
storedLayout.setPublishedDsl(publishedDsl);
|
||||||
|
storedLayout.setPublishedLayoutOnLoadActions(publishedLayoutOnLoadActions);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.setLayouts(layoutList);
|
||||||
|
return page;
|
||||||
|
})
|
||||||
|
.flatMap(pageService::save)
|
||||||
|
.flatMap(page -> {
|
||||||
|
List<Layout> layoutList = page.getLayouts();
|
||||||
|
for (Layout storedLayout : layoutList) {
|
||||||
|
if (storedLayout.getId().equals(layoutId)) {
|
||||||
|
return Mono.just(storedLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Mono.empty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of names of actions (nodes) and pageId, it hits the database and returns all the actions matching
|
||||||
|
* this criteria of name and pageId with http method 'GET'
|
||||||
|
*
|
||||||
|
* @param nodes
|
||||||
|
* @param pageId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Flux<Action> findRestApiActionsByPageIdAndHTTPMethodGET(Set<String> nodes, String pageId) {
|
||||||
|
|
||||||
|
return actionService
|
||||||
|
.findDistinctRestApiActionsByNameInAndPageIdAndHttpMethod(nodes, pageId, "GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Action> moveAction(ActionMoveDTO actionMoveDTO) {
|
||||||
|
Action action = actionMoveDTO.getAction();
|
||||||
|
|
||||||
|
String oldPageId = action.getPageId();
|
||||||
|
|
||||||
|
action.setPageId(actionMoveDTO.getDestinationPageId());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following steps are followed here :
|
||||||
|
* 1. Update and save the action
|
||||||
|
* 2. Run updateLayout on the old page
|
||||||
|
* 3. Run updateLayout on the new page.
|
||||||
|
* 4. Return the saved action.
|
||||||
|
*/
|
||||||
|
return actionService
|
||||||
|
.save(action)
|
||||||
|
.flatMap(savedAction -> pageService
|
||||||
|
.findById(oldPageId)
|
||||||
|
.map(page -> page.getLayouts()
|
||||||
|
.stream()
|
||||||
|
/*
|
||||||
|
* subscribe() is being used here because within a stream, the master subscriber provided
|
||||||
|
* by spring framework does not get attached here leading to the updateLayout mono not
|
||||||
|
* emitting. The same is true for the updateLayout call for the new page.
|
||||||
|
*/
|
||||||
|
.map(layout -> updateLayout(oldPageId, layout.getId(), layout).subscribe())
|
||||||
|
.collect(toSet()))
|
||||||
|
.then(pageService.findById(actionMoveDTO.getDestinationPageId()))
|
||||||
|
.map(page -> page.getLayouts()
|
||||||
|
.stream()
|
||||||
|
.map(layout -> updateLayout(actionMoveDTO.getDestinationPageId(), layout.getId(), layout).subscribe())
|
||||||
|
.collect(toSet()))
|
||||||
|
.thenReturn(savedAction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,4 @@ public interface LayoutService {
|
||||||
Mono<Layout> createLayout(String pageId, Layout layout);
|
Mono<Layout> createLayout(String pageId, Layout layout);
|
||||||
|
|
||||||
Mono<Layout> getLayout(String pageId, String layoutId, Boolean viewMode);
|
Mono<Layout> getLayout(String pageId, String layoutId, Boolean viewMode);
|
||||||
|
|
||||||
Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,20 @@
|
||||||
package com.appsmith.server.services;
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.domains.Action;
|
|
||||||
import com.appsmith.server.domains.Layout;
|
import com.appsmith.server.domains.Layout;
|
||||||
import com.appsmith.server.domains.Page;
|
import com.appsmith.server.domains.Page;
|
||||||
import com.appsmith.server.dtos.DslActionDTO;
|
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.minidev.json.JSONObject;
|
import net.minidev.json.JSONObject;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
import org.jgrapht.Graph;
|
import org.jgrapht.Graph;
|
||||||
import org.jgrapht.graph.DefaultEdge;
|
import org.jgrapht.graph.DefaultEdge;
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -29,7 +22,6 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static com.appsmith.server.helpers.MustacheHelper.extractMustacheKeys;
|
import static com.appsmith.server.helpers.MustacheHelper.extractMustacheKeys;
|
||||||
import static java.util.stream.Collectors.toSet;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
|
@ -37,7 +29,6 @@ public class LayoutServiceImpl implements LayoutService {
|
||||||
|
|
||||||
private final ApplicationPageService applicationPageService;
|
private final ApplicationPageService applicationPageService;
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final ActionService actionService;
|
|
||||||
/*
|
/*
|
||||||
* This pattern finds all the String which have been extracted from the mustache dynamic bindings.
|
* This pattern finds all the String which have been extracted from the mustache dynamic bindings.
|
||||||
* e.g. for the given JS function using action with name "fetchUsers"
|
* e.g. for the given JS function using action with name "fetchUsers"
|
||||||
|
|
@ -45,17 +36,12 @@ public class LayoutServiceImpl implements LayoutService {
|
||||||
* This pattern should return ["JSON.stringify", "fetchUsers"]
|
* This pattern should return ["JSON.stringify", "fetchUsers"]
|
||||||
*/
|
*/
|
||||||
private final Pattern pattern = Pattern.compile("[a-zA-Z0-9._]+");
|
private final Pattern pattern = Pattern.compile("[a-zA-Z0-9._]+");
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LayoutServiceImpl(ApplicationPageService applicationPageService,
|
public LayoutServiceImpl(ApplicationPageService applicationPageService,
|
||||||
PageService pageService,
|
PageService pageService) {
|
||||||
ActionService actionService,
|
|
||||||
ObjectMapper objectMapper) {
|
|
||||||
this.applicationPageService = applicationPageService;
|
this.applicationPageService = applicationPageService;
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.actionService = actionService;
|
|
||||||
this.objectMapper = objectMapper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -100,111 +86,6 @@ public class LayoutServiceImpl implements LayoutService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout) {
|
|
||||||
Set<String> dynamicBindingNames = new HashSet<>();
|
|
||||||
String dslString = "";
|
|
||||||
|
|
||||||
// Convert the DSL into a String
|
|
||||||
JSONObject dsl = layout.getDsl();
|
|
||||||
try {
|
|
||||||
dslString = objectMapper.writeValueAsString(dsl);
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.debug("Exception caught during conversion of DSL Json object to String. ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract all the mustache keys in the DSL to get the dynamic bindings used in the DSL.
|
|
||||||
Set<String> dynamicBindings = extractMustacheKeys(dslString);
|
|
||||||
|
|
||||||
if (!dynamicBindings.isEmpty()) {
|
|
||||||
for (String mustacheKey : dynamicBindings) {
|
|
||||||
String key = mustacheKey.trim();
|
|
||||||
|
|
||||||
// Extract all the words in the dynamic bindings
|
|
||||||
Matcher matcher = pattern.matcher(key);
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
|
||||||
String word = matcher.group();
|
|
||||||
|
|
||||||
String[] subStrings = word.split(Pattern.quote("."));
|
|
||||||
if (subStrings.length > 0 ) {
|
|
||||||
// We are only interested in the top level. e.g. if its Input1.text, we want just Input1
|
|
||||||
dynamicBindingNames.add(subStrings[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mono<Set<DslActionDTO>> onLoadActionsMono = findRestApiActionsByPageIdAndHTTPMethodGET(dynamicBindingNames, pageId)
|
|
||||||
.map(action -> {
|
|
||||||
// Since we are only interested in few fields, prepare the DslActionDTO that needs to be stored in
|
|
||||||
// the layout and return it to be collected in to a set.
|
|
||||||
DslActionDTO newAction = new DslActionDTO();
|
|
||||||
newAction.setId(action.getId());
|
|
||||||
newAction.setPluginType(action.getPluginType());
|
|
||||||
newAction.setJsonPathKeys(action.getJsonPathKeys());
|
|
||||||
newAction.setName(action.getName());
|
|
||||||
return newAction;
|
|
||||||
})
|
|
||||||
.collect(toSet());
|
|
||||||
|
|
||||||
return pageService.findByIdAndLayoutsId(pageId, layoutId)
|
|
||||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.PAGE_ID + " or " + FieldName.LAYOUT_ID)))
|
|
||||||
.flatMap(applicationPageService::doesPageBelongToCurrentUserOrganization)
|
|
||||||
//The pageId given is correct and belongs to the current user's organization.
|
|
||||||
.zipWith(onLoadActionsMono)
|
|
||||||
.map(tuple -> {
|
|
||||||
Page page = tuple.getT1();
|
|
||||||
Set<DslActionDTO> onLoadActions = tuple.getT2();
|
|
||||||
|
|
||||||
List<Layout> layoutList = page.getLayouts();
|
|
||||||
|
|
||||||
//Because the findByIdAndLayoutsId call returned non-empty result, we are guaranteed to find the layoutId here.
|
|
||||||
for (Layout storedLayout : layoutList) {
|
|
||||||
if (storedLayout.getId().equals(layoutId)) {
|
|
||||||
//Copy the variables to conserve before update
|
|
||||||
JSONObject publishedDsl = storedLayout.getPublishedDsl();
|
|
||||||
Set<DslActionDTO> publishedLayoutOnLoadActions = storedLayout.getPublishedLayoutOnLoadActions();
|
|
||||||
|
|
||||||
//Update
|
|
||||||
layout.setLayoutOnLoadActions(onLoadActions);
|
|
||||||
BeanUtils.copyProperties(layout, storedLayout);
|
|
||||||
storedLayout.setId(layoutId);
|
|
||||||
|
|
||||||
//Copy back the conserved variables.
|
|
||||||
storedLayout.setPublishedDsl(publishedDsl);
|
|
||||||
storedLayout.setPublishedLayoutOnLoadActions(publishedLayoutOnLoadActions);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page.setLayouts(layoutList);
|
|
||||||
return page;
|
|
||||||
})
|
|
||||||
.flatMap(pageService::save)
|
|
||||||
.flatMap(page -> {
|
|
||||||
List<Layout> layoutList = page.getLayouts();
|
|
||||||
for (Layout storedLayout : layoutList) {
|
|
||||||
if (storedLayout.getId().equals(layoutId)) {
|
|
||||||
return Mono.just(storedLayout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Mono.empty();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a list of names of actions (nodes) and pageId, it hits the database and returns all the actions matching
|
|
||||||
* this criteria of name and pageId with http method 'GET'
|
|
||||||
*
|
|
||||||
* @param nodes
|
|
||||||
* @param pageId
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Flux<Action> findRestApiActionsByPageIdAndHTTPMethodGET(Set<String> nodes, String pageId) {
|
|
||||||
|
|
||||||
return actionService
|
|
||||||
.findDistinctRestApiActionsByNameInAndPageIdAndHttpMethod(nodes, pageId, "GET");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walks the DSL and extracts all the widget names from it and adds it to the graph.
|
* Walks the DSL and extracts all the widget names from it and adds it to the graph.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ public class LayoutServiceTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
PageService pageService;
|
PageService pageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
LayoutActionService layoutActionService;
|
||||||
|
|
||||||
Mono<Layout> layoutMono;
|
Mono<Layout> layoutMono;
|
||||||
|
|
||||||
Mono<Application> applicationMono;
|
Mono<Application> applicationMono;
|
||||||
|
|
@ -134,7 +137,7 @@ public class LayoutServiceTest {
|
||||||
obj.put("key", "value-updated");
|
obj.put("key", "value-updated");
|
||||||
updateLayout.setDsl(obj);
|
updateLayout.setDsl(obj);
|
||||||
|
|
||||||
Mono<Layout> updatedLayoutMono = layoutService.updateLayout("random-impossible-id-page", startLayout.getId(), updateLayout);
|
Mono<Layout> updatedLayoutMono = layoutActionService.updateLayout("random-impossible-id-page", startLayout.getId(), updateLayout);
|
||||||
|
|
||||||
StepVerifier
|
StepVerifier
|
||||||
.create(updatedLayoutMono)
|
.create(updatedLayoutMono)
|
||||||
|
|
@ -164,7 +167,7 @@ public class LayoutServiceTest {
|
||||||
obj1.put("key1", "value-updated");
|
obj1.put("key1", "value-updated");
|
||||||
updateLayout.setDsl(obj);
|
updateLayout.setDsl(obj);
|
||||||
|
|
||||||
Mono<Layout> updatedLayoutMono = layoutService.updateLayout(page.getId(), startLayout.getId(), updateLayout);
|
Mono<Layout> updatedLayoutMono = layoutActionService.updateLayout(page.getId(), startLayout.getId(), updateLayout);
|
||||||
|
|
||||||
StepVerifier
|
StepVerifier
|
||||||
.create(updatedLayoutMono)
|
.create(updatedLayoutMono)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user