fix: Refactor entities with specific rules (#17523)
* Refactor changes for DSL * Spaces * Action and collection refactor logic * Changes to some logic for DSL * Fixed tests, added dynamic trigger path list logic as well * Added test for dynamicTriggerList condition * added analytics data to response in ast * Fix for peer closed connection on AST * Added comments for clarity * Added logs for time taken by AST call * handle export default and update success param accordingly * updates for review comments Co-authored-by: ChandanBalajiBP <chandan@appsmith.com>
This commit is contained in:
parent
c1e9bea10a
commit
204a187bc2
|
|
@ -13,6 +13,7 @@ type entityRefactorType = {
|
|||
script: string;
|
||||
oldName: string;
|
||||
newName: string;
|
||||
isJSObject: boolean;
|
||||
evalVersion?: number;
|
||||
};
|
||||
|
||||
|
|
@ -74,15 +75,16 @@ export default class AstController extends BaseController {
|
|||
async entityRefactorController(req: Request, res: Response) {
|
||||
try {
|
||||
// By default the application eval version is set to be 2
|
||||
const { script, oldName, newName, evalVersion }: entityRefactorType =
|
||||
const { script, oldName, newName, isJSObject, evalVersion }: entityRefactorType =
|
||||
req.body;
|
||||
const data = await AstService.entityRefactor(
|
||||
script,
|
||||
oldName,
|
||||
newName,
|
||||
isJSObject,
|
||||
evalVersion
|
||||
);
|
||||
return super.sendResponse(res, data);
|
||||
return super.sendEntityResponse(res, data.body, data.isSuccess);
|
||||
} catch (err) {
|
||||
return super.sendError(
|
||||
res,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,18 @@ export default class BaseController {
|
|||
});
|
||||
}
|
||||
|
||||
sendEntityResponse(
|
||||
response: Response,
|
||||
result?: unknown,
|
||||
success?: boolean,
|
||||
code: number = StatusCodes.OK
|
||||
): Response<ResponseData> {
|
||||
return response.status(code).json({
|
||||
success,
|
||||
data: result,
|
||||
});
|
||||
}
|
||||
|
||||
sendError(
|
||||
response: Response,
|
||||
error: string,
|
||||
|
|
|
|||
|
|
@ -12,4 +12,19 @@ export default class AstValidator {
|
|||
min: 1,
|
||||
})
|
||||
.withMessage("Multiple scripts are required");
|
||||
|
||||
static getEntityRefactorValidator = () => [
|
||||
body("script")
|
||||
.isString()
|
||||
.withMessage("Script is required and can only be a string"),
|
||||
body("oldName")
|
||||
.isString()
|
||||
.withMessage("OldName is required and can only be a string"),
|
||||
body("newName")
|
||||
.isString()
|
||||
.withMessage("NewName is required and can only be a string"),
|
||||
body("isJSObject")
|
||||
.isBoolean()
|
||||
.withMessage("isJSObject is required and can only be a boolean"),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ router.post(
|
|||
);
|
||||
router.post(
|
||||
"/entity-refactor",
|
||||
AstRules.getScriptValidator(),
|
||||
AstRules.getEntityRefactorValidator(),
|
||||
validator.validateRequest,
|
||||
astController.entityRefactorController
|
||||
);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export default class AstService {
|
|||
script,
|
||||
oldName,
|
||||
newName,
|
||||
isJSObject,
|
||||
evalVersion
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
@ -33,6 +34,7 @@ export default class AstService {
|
|||
script,
|
||||
oldName,
|
||||
newName,
|
||||
isJSObject,
|
||||
evalVersion
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,23 +18,37 @@ const entityRefactor = [
|
|||
script: "ApiNever",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: false,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
script: "ApiNever.data",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: false,
|
||||
},
|
||||
{
|
||||
script:
|
||||
"// ApiNever \n function ApiNever(abc) {let foo = \"I'm getting data from ApiNever but don't rename this string\" + ApiNever.data; \n if(true) { return ApiNever }}",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: false,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
script:
|
||||
"//ApiNever \n function ApiNever(abc) {let ApiNever = \"I'm getting data from ApiNever but don't rename this string\" + ApiNever.data; \n if(true) { return ApiNever }}",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: false,
|
||||
},
|
||||
{
|
||||
script:
|
||||
"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\t\tsearch: () => {\n\t\tif(Input1Copy.text.length==0){\n\t\t\treturn select_repair_db.data\n\t\t}\n\t\telse{\n\t\t\treturn(select_repair_db.data.filter(word => word.cust_name.toLowerCase().includes(Input1Copy.text.toLowerCase())))\n\t\t}\n\t},\n}",
|
||||
oldName: "Input1Copy",
|
||||
newName: "Input1",
|
||||
isJSObject: true,
|
||||
evalVersion: 2,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -93,17 +107,22 @@ describe("AST tests", () => {
|
|||
entityRefactor.forEach(async (input, index) => {
|
||||
it(`Entity refactor test case ${index + 1}`, async () => {
|
||||
const expectedResponse = [
|
||||
{ script: "ApiForever", count: 1 },
|
||||
{ script: "ApiForever.data", count: 1 },
|
||||
{ script: "ApiForever", refactorCount: 1 },
|
||||
{ script: "ApiForever.data", refactorCount: 1 },
|
||||
{
|
||||
script:
|
||||
"// ApiNever \n function ApiNever(abc) {let foo = \"I'm getting data from ApiNever but don't rename this string\" + ApiForever.data; \n if(true) { return ApiForever }}",
|
||||
count: 2,
|
||||
refactorCount: 2,
|
||||
},
|
||||
{
|
||||
script:
|
||||
"//ApiNever \n function ApiNever(abc) {let ApiNever = \"I'm getting data from ApiNever but don't rename this string\" + ApiNever.data; \n if(true) { return ApiNever }}",
|
||||
count: 0,
|
||||
refactorCount: 0,
|
||||
},
|
||||
{
|
||||
script:
|
||||
"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\t\tsearch: () => {\n\t\tif(Input1.text.length==0){\n\t\t\treturn select_repair_db.data\n\t\t}\n\t\telse{\n\t\t\treturn(select_repair_db.data.filter(word => word.cust_name.toLowerCase().includes(Input1.text.toLowerCase())))\n\t\t}\n\t},\n}",
|
||||
refactorCount: 2,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -118,10 +137,31 @@ describe("AST tests", () => {
|
|||
expect(response.body.data.script).toEqual(
|
||||
expectedResponse[index].script
|
||||
);
|
||||
expect(response.body.data.count).toEqual(
|
||||
expectedResponse[index].count
|
||||
expect(response.body.data.refactorCount).toEqual(
|
||||
expectedResponse[index].refactorCount
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Entity refactor syntax error", async () => {
|
||||
let request = {
|
||||
script: "ApiNever++++",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: true,
|
||||
evalVersion: 2,
|
||||
};
|
||||
|
||||
await supertest(app)
|
||||
.post(`${RTS_BASE_API_PATH}/ast/entity-refactor`, {
|
||||
JSON: true,
|
||||
})
|
||||
.send(request)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.success).toEqual(false);
|
||||
expect(response.body.data.error).toEqual("Syntax Error");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.resources.ConnectionProvider;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
|
|
@ -35,12 +36,23 @@ public class WebClientUtils {
|
|||
.build();
|
||||
}
|
||||
|
||||
public static WebClient create(ConnectionProvider provider) {
|
||||
return builder(provider)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static WebClient create(String baseUrl) {
|
||||
return builder()
|
||||
.baseUrl(baseUrl)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static WebClient create(String baseUrl, ConnectionProvider provider) {
|
||||
return builder(provider)
|
||||
.baseUrl(baseUrl)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static boolean shouldUseSystemProxy() {
|
||||
return "true".equals(System.getProperty("java.net.useSystemProxies"))
|
||||
&& (!System.getProperty("http.proxyHost", "").isEmpty() || !System.getProperty("https.proxyHost", "").isEmpty());
|
||||
|
|
@ -50,6 +62,10 @@ public class WebClientUtils {
|
|||
return builder(HttpClient.create());
|
||||
}
|
||||
|
||||
public static WebClient.Builder builder(ConnectionProvider provider) {
|
||||
return builder(HttpClient.create(provider));
|
||||
}
|
||||
|
||||
public static WebClient.Builder builder(HttpClient httpClient) {
|
||||
return WebClient.builder()
|
||||
.clientConnector(new ReactorClientHttpConnector(makeSafeHttpClient(httpClient)));
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ public class FieldName {
|
|||
public static String PUBLISHED_APPLICATION = "deployed application";
|
||||
public static final String TOKEN = "token";
|
||||
public static String WIDGET_TYPE = "type";
|
||||
public static String LIST_WIDGET_TEMPLATE = "template";
|
||||
public static String LIST_WIDGET = "LIST_WIDGET";
|
||||
public static String TABLE_WIDGET = "TABLE_WIDGET";
|
||||
public static String CONTAINER_WIDGET = "CONTAINER_WIDGET";
|
||||
public static String CANVAS_WIDGET = "CANVAS_WIDGET";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
package com.appsmith.server.helpers;
|
||||
|
||||
import com.appsmith.external.helpers.MustacheHelper;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DslUtils {
|
||||
|
||||
public static Set<String> getMustacheValueSetFromSpecificDynamicBindingPath(JsonNode dsl, String fieldPath) {
|
||||
|
||||
DslNodeWalkResponse dslWalkResponse = getDslWalkResponse(dsl, fieldPath);
|
||||
|
||||
|
||||
// Only extract mustache keys from leaf nodes
|
||||
if (dslWalkResponse != null && dslWalkResponse.isLeafNode) {
|
||||
|
||||
// We found the path. But if the path does not have any mustache bindings, return with empty set
|
||||
if (!MustacheHelper.laxIsBindingPresentInString(((TextNode) dslWalkResponse.currentNode).asText())) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
// Stricter extraction of dynamic bindings
|
||||
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(((TextNode) dslWalkResponse.currentNode).asText());
|
||||
return mustacheKeysFromFields;
|
||||
}
|
||||
|
||||
// This was not a text node, we do not know how to handle this
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
public static JsonNode replaceValuesInSpecificDynamicBindingPath(JsonNode dsl, String fieldPath, Map<String, String> replacementMap) {
|
||||
DslNodeWalkResponse dslWalkResponse = getDslWalkResponse(dsl, fieldPath);
|
||||
|
||||
if (dslWalkResponse != null && dslWalkResponse.isLeafNode) {
|
||||
final String oldValue = ((TextNode) dslWalkResponse.currentNode).asText();
|
||||
|
||||
final String newValue = StringUtils.replaceEach(
|
||||
oldValue,
|
||||
replacementMap.keySet().toArray(new String[0]),
|
||||
replacementMap.values().toArray(new String[0]));
|
||||
|
||||
((ObjectNode) dslWalkResponse.parentNode).set(dslWalkResponse.currentKey, new TextNode(newValue));
|
||||
}
|
||||
return dsl;
|
||||
}
|
||||
|
||||
private static DslNodeWalkResponse getDslWalkResponse(JsonNode dsl, String fieldPath) {
|
||||
String[] fields = fieldPath.split("[].\\[]");
|
||||
// For nested fields, the parent dsl to search in would shift by one level every iteration
|
||||
Object currentNode = dsl;
|
||||
Object parent = null;
|
||||
Iterator<String> fieldsIterator = Arrays.stream(fields).filter(fieldToken -> !fieldToken.isBlank()).iterator();
|
||||
boolean isLeafNode = false;
|
||||
String nextKey = null;
|
||||
// This loop will end at either a leaf node, or the last identified JSON field (by throwing an exception)
|
||||
// Valid forms of the fieldPath for this search could be:
|
||||
// root.field.list[index].childField.anotherList.indexWithDotOperator.multidimensionalList[index1][index2]
|
||||
while (fieldsIterator.hasNext()) {
|
||||
nextKey = fieldsIterator.next();
|
||||
parent = currentNode;
|
||||
if (currentNode instanceof ArrayNode) {
|
||||
if (Pattern.matches(Pattern.compile("[0-9]+").toString(), nextKey)) {
|
||||
try {
|
||||
currentNode = ((ArrayNode) currentNode).get(Integer.parseInt(nextKey));
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// The index being referred does not exist, hence the path would not exist.
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// This is an array but the fieldPath does not have an index to refer to
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
currentNode = ((JsonNode) currentNode).get(nextKey);
|
||||
}
|
||||
// After updating the currentNode, check for the types
|
||||
if (currentNode == null) {
|
||||
return null;
|
||||
} else if (currentNode instanceof TextNode) {
|
||||
// If we get String value, then this is a leaf node
|
||||
isLeafNode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new DslNodeWalkResponse(currentNode, parent, nextKey, isLeafNode);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private static class DslNodeWalkResponse {
|
||||
Object currentNode;
|
||||
Object parentNode;
|
||||
String currentKey;
|
||||
Boolean isLeafNode;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.appsmith.server.services.ce;
|
|||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface AstServiceCE {
|
||||
|
|
@ -18,4 +19,6 @@ public interface AstServiceCE {
|
|||
* @return A mono of list of strings that represent all valid global references in the binding string
|
||||
*/
|
||||
Mono<Set<String>> getPossibleReferencesFromDynamicBinding(String bindingValue, int evalVersion);
|
||||
|
||||
Mono<Map<String, String>> refactorNameInDynamicBindings(Set<String> bindingValues, String oldName, String newName, int evalVersion);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,17 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.netty.resources.ConnectionProvider;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
|
|
@ -26,6 +33,15 @@ public class AstServiceCEImpl implements AstServiceCE {
|
|||
|
||||
private final InstanceConfig instanceConfig;
|
||||
|
||||
private final WebClient webClient = WebClientUtils.create(ConnectionProvider.builder("rts-provider")
|
||||
.maxConnections(500)
|
||||
.maxIdleTime(Duration.ofSeconds(5))
|
||||
.maxLifeTime(Duration.ofSeconds(10))
|
||||
.pendingAcquireTimeout(Duration.ofSeconds(10))
|
||||
.evictInBackground(Duration.ofSeconds(20)).build());
|
||||
|
||||
private final static long MAX_API_RESPONSE_TIME = 50;
|
||||
|
||||
@Override
|
||||
public Mono<Set<String>> getPossibleReferencesFromDynamicBinding(String bindingValue, int evalVersion) {
|
||||
if (!StringUtils.hasLength(bindingValue)) {
|
||||
|
|
@ -38,16 +54,61 @@ public class AstServiceCEImpl implements AstServiceCE {
|
|||
return Mono.just(new HashSet<>(MustacheHelper.getPossibleParentsOld(bindingValue)));
|
||||
}
|
||||
|
||||
return WebClientUtils.create(commonConfig.getRtsBaseDomain() + "/rts-api/v1/ast/single-script-data")
|
||||
return webClient
|
||||
.post()
|
||||
.uri(commonConfig.getRtsBaseDomain() + "/rts-api/v1/ast/single-script-data")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(new GetIdentifiersRequest(bindingValue, evalVersion)))
|
||||
.retrieve()
|
||||
.bodyToMono(GetIdentifiersResponse.class)
|
||||
.elapsed()
|
||||
.map(tuple -> {
|
||||
log.debug("Time elapsed since AST get identifiers call: {} ms", tuple.getT1());
|
||||
if (tuple.getT1() > MAX_API_RESPONSE_TIME) {
|
||||
log.debug("This call took longer than expected. The binding was: {}", bindingValue);
|
||||
}
|
||||
return tuple.getT2();
|
||||
})
|
||||
.map(response -> response.data.references);
|
||||
// TODO: add error handling scenario for when RTS is not accessible in fat container
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Map<String, String>> refactorNameInDynamicBindings(Set<String> bindingValues, String oldName, String newName, int evalVersion) {
|
||||
if (bindingValues == null || bindingValues.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return Flux.fromIterable(bindingValues)
|
||||
.flatMap(bindingValue -> {
|
||||
return webClient
|
||||
.post()
|
||||
.uri(commonConfig.getRtsBaseDomain() + "/rts-api/v1/ast/entity-refactor")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(new EntityRefactorRequest(bindingValue, oldName, newName, evalVersion)))
|
||||
.retrieve()
|
||||
.bodyToMono(EntityRefactorResponse.class)
|
||||
.elapsed()
|
||||
.map(tuple -> {
|
||||
log.debug("Time elapsed since AST refactor call: {} ms", tuple.getT1());
|
||||
if (tuple.getT1() > MAX_API_RESPONSE_TIME) {
|
||||
log.debug("This call took longer than expected. The binding was: {}", bindingValue);
|
||||
}
|
||||
return tuple.getT2();
|
||||
})
|
||||
.map(EntityRefactorResponse::getData)
|
||||
.filter(details -> details.refactorCount > 0)
|
||||
.flatMap(response -> Mono.just(bindingValue).zipWith(Mono.just(response.script)))
|
||||
.onErrorResume(error -> {
|
||||
var temp = bindingValue;
|
||||
// If there is a problem with parsing and refactoring this binding, we just ignore it and move ahead
|
||||
// The expectation is that this binding would error out during eval anyway
|
||||
return Mono.empty();
|
||||
});
|
||||
})
|
||||
.collect(Collectors.toMap(Tuple2::getT1, Tuple2::getT2));
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
|
|
@ -90,4 +151,32 @@ public class AstServiceCEImpl implements AstServiceCE {
|
|||
Set<String> functionalParams;
|
||||
Set<String> variables;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
static class EntityRefactorRequest {
|
||||
String script;
|
||||
String oldName;
|
||||
String newName;
|
||||
int evalVersion;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
static class EntityRefactorResponse {
|
||||
EntityRefactorResponseDetails data;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
static class EntityRefactorResponseDetails {
|
||||
String script;
|
||||
int referenceCount;
|
||||
int refactorCount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.appsmith.server.solutions;
|
||||
|
||||
import com.appsmith.server.configurations.InstanceConfig;
|
||||
import com.appsmith.server.helpers.ResponseUtils;
|
||||
import com.appsmith.server.services.ActionCollectionService;
|
||||
import com.appsmith.server.services.ApplicationService;
|
||||
import com.appsmith.server.services.AstService;
|
||||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.NewActionService;
|
||||
import com.appsmith.server.services.NewPageService;
|
||||
|
|
@ -19,12 +22,18 @@ public class RefactoringSolutionImpl extends RefactoringSolutionCEImpl implement
|
|||
NewActionService newActionService,
|
||||
ActionCollectionService actionCollectionService,
|
||||
ResponseUtils responseUtils,
|
||||
LayoutActionService layoutActionService) {
|
||||
LayoutActionService layoutActionService,
|
||||
ApplicationService applicationService,
|
||||
AstService astService,
|
||||
InstanceConfig instanceConfig) {
|
||||
super(objectMapper,
|
||||
newPageService,
|
||||
newActionService,
|
||||
actionCollectionService,
|
||||
responseUtils,
|
||||
layoutActionService);
|
||||
layoutActionService,
|
||||
applicationService,
|
||||
astService,
|
||||
instanceConfig);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.appsmith.server.solutions.ce;
|
|||
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.external.models.PluginType;
|
||||
import com.appsmith.server.configurations.InstanceConfig;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Layout;
|
||||
import com.appsmith.server.domains.NewAction;
|
||||
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||
|
|
@ -11,8 +14,11 @@ import com.appsmith.server.dtos.RefactorActionNameDTO;
|
|||
import com.appsmith.server.dtos.RefactorNameDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.server.helpers.DslUtils;
|
||||
import com.appsmith.server.helpers.ResponseUtils;
|
||||
import com.appsmith.server.services.ActionCollectionService;
|
||||
import com.appsmith.server.services.ApplicationService;
|
||||
import com.appsmith.server.services.AstService;
|
||||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.NewActionService;
|
||||
import com.appsmith.server.services.NewPageService;
|
||||
|
|
@ -21,44 +27,78 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
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 org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||
import static com.appsmith.server.services.ce.ApplicationPageServiceCEImpl.EVALUATION_VERSION;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final NewPageService newPageService;
|
||||
private final NewActionService newActionService;
|
||||
private final ActionCollectionService actionCollectionService;
|
||||
private final ResponseUtils responseUtils;
|
||||
private final LayoutActionService layoutActionService;
|
||||
private final ApplicationService applicationService;
|
||||
private final AstService astService;
|
||||
private final InstanceConfig instanceConfig;
|
||||
private final Boolean isRtsAccessible;
|
||||
|
||||
private static final Pattern actionCollectionBodyPattern = Pattern.compile("export default(.*)", Pattern.DOTALL);
|
||||
private static final String EXPORT_DEFAULT_STRING = "export default";
|
||||
|
||||
/*
|
||||
* To replace fetchUsers in `{{JSON.stringify(fetchUsers)}}` with getUsers, the following regex is required :
|
||||
* `\\b(fetchUsers)\\b`. To achieve this the following strings preWord and postWord are declared here to be used
|
||||
* at run time to create the regex pattern.
|
||||
*/
|
||||
private final String preWord = "\\b(";
|
||||
private final String postWord = ")\\b";
|
||||
private static final String preWord = "\\b(";
|
||||
private static final String postWord = ")\\b";
|
||||
|
||||
public RefactoringSolutionCEImpl(ObjectMapper objectMapper,
|
||||
NewPageService newPageService,
|
||||
NewActionService newActionService,
|
||||
ActionCollectionService actionCollectionService,
|
||||
ResponseUtils responseUtils,
|
||||
LayoutActionService layoutActionService,
|
||||
ApplicationService applicationService,
|
||||
AstService astService,
|
||||
InstanceConfig instanceConfig) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.newPageService = newPageService;
|
||||
this.newActionService = newActionService;
|
||||
this.actionCollectionService = actionCollectionService;
|
||||
this.responseUtils = responseUtils;
|
||||
this.layoutActionService = layoutActionService;
|
||||
this.applicationService = applicationService;
|
||||
this.astService = astService;
|
||||
this.instanceConfig = instanceConfig;
|
||||
|
||||
// TODO Remove this variable and access the field directly when RTS API is ready
|
||||
this.isRtsAccessible = false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorWidgetName(RefactorNameDTO refactorNameDTO) {
|
||||
|
|
@ -77,7 +117,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorWidgetName(RefactorNameDTO refactorNameDTO, String branchName) {
|
||||
if (StringUtils.isEmpty(branchName)) {
|
||||
if (!StringUtils.hasLength(branchName)) {
|
||||
return refactorWidgetName(refactorNameDTO);
|
||||
}
|
||||
|
||||
|
|
@ -94,13 +134,13 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
String pageId = refactorActionNameDTO.getPageId();
|
||||
String layoutId = refactorActionNameDTO.getLayoutId();
|
||||
String oldName = refactorActionNameDTO.getOldName();
|
||||
final String oldFullyQualifiedName = StringUtils.isEmpty(refactorActionNameDTO.getCollectionName()) ?
|
||||
oldName :
|
||||
refactorActionNameDTO.getCollectionName() + "." + oldName;
|
||||
final String oldFullyQualifiedName = StringUtils.hasLength(refactorActionNameDTO.getCollectionName()) ?
|
||||
refactorActionNameDTO.getCollectionName() + "." + oldName :
|
||||
oldName;
|
||||
String newName = refactorActionNameDTO.getNewName();
|
||||
final String newFullyQualifiedName = StringUtils.isEmpty(refactorActionNameDTO.getCollectionName()) ?
|
||||
newName :
|
||||
refactorActionNameDTO.getCollectionName() + "." + newName;
|
||||
final String newFullyQualifiedName = StringUtils.hasLength(refactorActionNameDTO.getCollectionName()) ?
|
||||
refactorActionNameDTO.getCollectionName() + "." + newName :
|
||||
newName;
|
||||
String actionId = refactorActionNameDTO.getActionId();
|
||||
return Mono.just(newActionService.validateActionName(newName))
|
||||
.flatMap(isValidName -> {
|
||||
|
|
@ -118,7 +158,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
})
|
||||
.flatMap(action -> {
|
||||
action.setName(newName);
|
||||
if (!StringUtils.isEmpty(refactorActionNameDTO.getCollectionName())) {
|
||||
if (StringUtils.hasLength(refactorActionNameDTO.getCollectionName())) {
|
||||
action.setFullyQualifiedName(newFullyQualifiedName);
|
||||
}
|
||||
return newActionService.updateUnpublishedAction(actionId, action);
|
||||
|
|
@ -139,34 +179,48 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
.map(responseUtils::updateLayoutDTOWithDefaultResources);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assumption here is that the refactoring name provided is indeed unique and is fit to be replaced everywhere.
|
||||
* <p>
|
||||
* At this point, the user must have MANAGE_PAGES and MANAGE_ACTIONS permissions for page and action respectively
|
||||
*
|
||||
* @param pageId
|
||||
* @param layoutId
|
||||
* @param oldName
|
||||
* @param newName
|
||||
* @return
|
||||
* @param pageId : The page that this entity belongs to
|
||||
* @param layoutId : The layout to parse through for replacement
|
||||
* @param oldName : The original name to look for
|
||||
* @param newName : The new name to refactor all references to
|
||||
* @return : The DSL after refactor updates
|
||||
*/
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorName(String pageId, String layoutId, String oldName, String newName) {
|
||||
String regexPattern = preWord + oldName + postWord;
|
||||
Pattern oldNamePattern = Pattern.compile(regexPattern);
|
||||
|
||||
Mono<PageDTO> updatePageMono = newPageService
|
||||
Mono<PageDTO> pageMono = newPageService
|
||||
// fetch the unpublished page
|
||||
.findPageById(pageId, MANAGE_PAGES, false)
|
||||
.cache();
|
||||
|
||||
Mono<Integer> evalVersionMono = pageMono
|
||||
.flatMap(page -> {
|
||||
return applicationService.findById(page.getApplicationId())
|
||||
.map(application -> {
|
||||
Integer evaluationVersion = application.getEvaluationVersion();
|
||||
if (evaluationVersion == null) {
|
||||
evaluationVersion = EVALUATION_VERSION;
|
||||
}
|
||||
return evaluationVersion;
|
||||
});
|
||||
})
|
||||
.cache();
|
||||
|
||||
Mono<PageDTO> updatePageMono = Mono.zip(pageMono, evalVersionMono)
|
||||
.flatMap(tuple -> {
|
||||
PageDTO page = tuple.getT1();
|
||||
int evalVersion = tuple.getT2();
|
||||
|
||||
List<Layout> layouts = page.getLayouts();
|
||||
for (Layout layout : layouts) {
|
||||
if (layoutId.equals(layout.getId()) && layout.getDsl() != null) {
|
||||
final JsonNode dslNode = objectMapper.convertValue(layout.getDsl(), JsonNode.class);
|
||||
final JsonNode dslNodeAfterReplacement = this.replaceStringInJsonNode(dslNode, oldNamePattern, newName);
|
||||
layout.setDsl(objectMapper.convertValue(dslNodeAfterReplacement, JSONObject.class));
|
||||
|
||||
// DSL has removed all the old names and replaced it with new name. If the change of name
|
||||
// was one of the mongoEscaped widgets, then update the names in the set as well
|
||||
Set<String> mongoEscapedWidgetNames = layout.getMongoEscapedWidgetNames();
|
||||
|
|
@ -174,9 +228,18 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
mongoEscapedWidgetNames.remove(oldName);
|
||||
mongoEscapedWidgetNames.add(newName);
|
||||
}
|
||||
page.setLayouts(layouts);
|
||||
|
||||
final JsonNode dslNode = objectMapper.convertValue(layout.getDsl(), JsonNode.class);
|
||||
Mono<PageDTO> refactorNameInDslMono = this.refactorNameInDsl(dslNode, oldName, newName, evalVersion, oldNamePattern)
|
||||
.then(Mono.fromCallable(() -> {
|
||||
layout.setDsl(objectMapper.convertValue(dslNode, JSONObject.class));
|
||||
page.setLayouts(layouts);
|
||||
return page;
|
||||
}));
|
||||
|
||||
// Since the page has most probably changed, save the page and return.
|
||||
return newPageService.saveUnpublishedPage(page);
|
||||
return refactorNameInDslMono
|
||||
.flatMap(newPageService::saveUnpublishedPage);
|
||||
}
|
||||
}
|
||||
// If we have reached here, the layout was not found and the page should be returned as is.
|
||||
|
|
@ -187,62 +250,76 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
|
||||
Mono<Set<String>> updateActionsMono = newActionService
|
||||
.findByPageIdAndViewMode(pageId, false, MANAGE_ACTIONS)
|
||||
.flatMap(newAction -> Mono.just(newAction).zipWith(evalVersionMono))
|
||||
/*
|
||||
* Assuming that the datasource should not be dependent on the widget and hence not going through the same
|
||||
* to look for replacement pattern.
|
||||
*/
|
||||
.flatMap(newAction1 -> {
|
||||
final NewAction newAction = newAction1;
|
||||
.flatMap(tuple -> {
|
||||
final NewAction newAction = tuple.getT1();
|
||||
final Integer evalVersion = tuple.getT2();
|
||||
// We need actionDTO to be populated with pluginType from NewAction
|
||||
// so that we can check for the JS path
|
||||
Mono<ActionDTO> actionMono = newActionService.generateActionByViewMode(newAction, false);
|
||||
return actionMono.flatMap(action -> {
|
||||
newAction.setUnpublishedAction(action);
|
||||
boolean actionUpdateRequired = false;
|
||||
ActionConfiguration actionConfiguration = action.getActionConfiguration();
|
||||
Set<String> jsonPathKeys = action.getJsonPathKeys();
|
||||
|
||||
if (jsonPathKeys != null && !jsonPathKeys.isEmpty()) {
|
||||
// Since json path keys actually contain the entire inline js function instead of just the widget/action
|
||||
// name, we can not simply use the set.contains(obj) function. We need to iterate over all the keys
|
||||
// in the set and see if the old name is a substring of the json path key.
|
||||
for (String key : jsonPathKeys) {
|
||||
if (oldNamePattern.matcher(key).find()) {
|
||||
actionUpdateRequired = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!actionUpdateRequired || actionConfiguration == null) {
|
||||
if (action.getActionConfiguration() == null) {
|
||||
return Mono.just(newAction);
|
||||
}
|
||||
// if actionUpdateRequired is true AND actionConfiguration is not null
|
||||
if (action.getCollectionId() != null) {
|
||||
// If this is a JS function rename, add this collection for rename
|
||||
// because the action configuration won't tell us this
|
||||
if (StringUtils.hasLength(action.getCollectionId()) && newName.equals(action.getValidName())) {
|
||||
updatableCollectionIds.add(action.getCollectionId());
|
||||
}
|
||||
final JsonNode actionConfigurationNode = objectMapper.convertValue(actionConfiguration, JsonNode.class);
|
||||
final JsonNode actionConfigurationNodeAfterReplacement = replaceStringInJsonNode(actionConfigurationNode, oldNamePattern, newName);
|
||||
|
||||
ActionConfiguration newActionConfiguration = objectMapper.convertValue(actionConfigurationNodeAfterReplacement, ActionConfiguration.class);
|
||||
action.setActionConfiguration(newActionConfiguration);
|
||||
NewAction newAction2 = newActionService.extractAndSetJsonPathKeys(newAction);
|
||||
return newActionService.save(newAction2);
|
||||
newAction.setUnpublishedAction(action);
|
||||
return this.refactorNameInAction(action, oldName, newName, evalVersion, oldNamePattern)
|
||||
.flatMap(updates -> {
|
||||
if (updates.isEmpty()) {
|
||||
return Mono.just(newAction);
|
||||
}
|
||||
if (StringUtils.hasLength(action.getCollectionId())) {
|
||||
updatableCollectionIds.add(action.getCollectionId());
|
||||
}
|
||||
newActionService.extractAndSetJsonPathKeys(newAction);
|
||||
return newActionService.save(newAction);
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
.map(savedAction -> savedAction.getUnpublishedAction().getName())
|
||||
.collect(toSet())
|
||||
.flatMap(updatedActions -> {
|
||||
.zipWith(evalVersionMono)
|
||||
.flatMap(tuple -> {
|
||||
Set<String> updatedActions = tuple.getT1();
|
||||
Integer evalVersion = tuple.getT2();
|
||||
// If these actions belonged to collections, update the collection body
|
||||
return Flux.fromIterable(updatableCollectionIds)
|
||||
.flatMap(collectionId -> actionCollectionService.findById(collectionId, MANAGE_ACTIONS))
|
||||
.flatMap(actionCollection -> {
|
||||
final ActionCollectionDTO unpublishedCollection = actionCollection.getUnpublishedCollection();
|
||||
Matcher matcher = oldNamePattern.matcher(unpublishedCollection.getBody());
|
||||
String newBodyAsString = matcher.replaceAll(newName);
|
||||
unpublishedCollection.setBody(newBodyAsString);
|
||||
return actionCollectionService.save(actionCollection);
|
||||
|
||||
Matcher matcher = actionCollectionBodyPattern.matcher(unpublishedCollection.getBody());
|
||||
if (matcher.find()) {
|
||||
String parsableBody = matcher.group(1);
|
||||
return this.replaceValueInMustacheKeys(
|
||||
new HashSet<>(Collections.singletonList(parsableBody)),
|
||||
oldName,
|
||||
newName,
|
||||
evalVersion,
|
||||
oldNamePattern)
|
||||
.flatMap(replacedMap -> {
|
||||
Optional<String> replacedValue = replacedMap.values().stream().findFirst();
|
||||
// This value should always be there
|
||||
if (replacedValue.isPresent()) {
|
||||
final String replacedBody = EXPORT_DEFAULT_STRING + replacedValue.get();
|
||||
unpublishedCollection.setBody(replacedBody);
|
||||
return actionCollectionService.save(actionCollection);
|
||||
}
|
||||
return Mono.just(actionCollection);
|
||||
});
|
||||
} else {
|
||||
// TODO make this error more informative, users should never edit JS objects to this state
|
||||
return Mono.error(new AppsmithException(AppsmithError.INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
})
|
||||
.collectList()
|
||||
.thenReturn(updatedActions);
|
||||
|
|
@ -264,59 +341,248 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
});
|
||||
}
|
||||
|
||||
Mono<Set<String>> refactorNameInDsl(JsonNode dsl, String oldName, String newName, int evalVersion, Pattern oldNamePattern) {
|
||||
|
||||
private JsonNode replaceStringInJsonNode(JsonNode jsonNode, Pattern oldNamePattern, String newName) {
|
||||
// If this is a text node, perform replacement directly
|
||||
if (jsonNode.isTextual()) {
|
||||
Matcher matcher = oldNamePattern.matcher(jsonNode.asText());
|
||||
String valueAfterReplacement = matcher.replaceAll(newName);
|
||||
return new TextNode(valueAfterReplacement);
|
||||
Mono<Set<String>> refactorNameInWidgetMono = Mono.just(new HashSet<>());
|
||||
Mono<Set<String>> recursiveRefactorNameInDslMono = Mono.just(new HashSet<>());
|
||||
|
||||
// if current object is widget,
|
||||
if (dsl.has(FieldName.WIDGET_ID)) {
|
||||
// enter parse widget method
|
||||
refactorNameInWidgetMono = refactorNameInWidget(dsl, oldName, newName, evalVersion, oldNamePattern);
|
||||
}
|
||||
// if current object has children,
|
||||
if (dsl.has("children")) {
|
||||
ArrayNode dslChildren = (ArrayNode) dsl.get("children");
|
||||
// recurse over each child
|
||||
recursiveRefactorNameInDslMono = Flux.fromStream(StreamSupport.stream(dslChildren.spliterator(), true))
|
||||
.flatMap(child -> refactorNameInDsl(child, oldName, newName, evalVersion, oldNamePattern))
|
||||
.reduce(new HashSet<>(), (x, y) -> {
|
||||
// for each child, aggregate the refactored paths
|
||||
y.addAll(x);
|
||||
return y;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO This is special handling for the list widget that has been added to allow refactoring of
|
||||
// just the default widgets inside the list. This is required because for the list, the widget names
|
||||
// exist as keys at the location List1.template(.Text1) [Ref #9281]
|
||||
// Ideally, we should avoid any non-structural elements as keys. This will be improved in list widget v2
|
||||
if (jsonNode.has("type") && "LIST_WIDGET".equals(jsonNode.get("type").asText())) {
|
||||
final JsonNode template = jsonNode.get("template");
|
||||
return refactorNameInWidgetMono
|
||||
.zipWith(recursiveRefactorNameInDslMono)
|
||||
.map(tuple -> {
|
||||
tuple.getT1().addAll(tuple.getT2());
|
||||
return tuple.getT1();
|
||||
});
|
||||
}
|
||||
|
||||
Mono<Set<String>> refactorNameInWidget(JsonNode widgetDsl, String oldName, String newName, int evalVersion, Pattern oldNamePattern) {
|
||||
boolean isRefactoredWidget = false;
|
||||
boolean isRefactoredTemplate = false;
|
||||
String widgetName = "";
|
||||
// If the name of this widget matches the old name, replace the name
|
||||
if (widgetDsl.has(FieldName.WIDGET_NAME)) {
|
||||
widgetName = widgetDsl.get(FieldName.WIDGET_NAME).asText();
|
||||
if (oldName.equals(widgetName)) {
|
||||
((ObjectNode) widgetDsl).set(FieldName.WIDGET_NAME, new TextNode(newName));
|
||||
// We mark this widget name as being a path that was refactored using this boolean value
|
||||
isRefactoredWidget = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This is special handling for the list widget that has been added to allow refactoring of
|
||||
// just the default widgets inside the list. This is required because for the list, the widget names
|
||||
// exist as keys at the location List1.template(.Text1) [Ref #9281]
|
||||
// Ideally, we should avoid any non-structural elements as keys. This will be improved in list widget v2
|
||||
if (widgetDsl.has(FieldName.WIDGET_TYPE) && FieldName.LIST_WIDGET.equals(widgetDsl.get(FieldName.WIDGET_TYPE).asText())) {
|
||||
final JsonNode template = widgetDsl.get(FieldName.LIST_WIDGET_TEMPLATE);
|
||||
JsonNode newJsonNode = null;
|
||||
String fieldName = null;
|
||||
final Iterator<String> templateIterator = template.fieldNames();
|
||||
while (templateIterator.hasNext()) {
|
||||
fieldName = templateIterator.next();
|
||||
|
||||
// For each element within template, check whether it would match the replacement pattern
|
||||
final Matcher listWidgetTemplateKeyMatcher = oldNamePattern.matcher(fieldName);
|
||||
if (listWidgetTemplateKeyMatcher.find()) {
|
||||
if (oldName.equals(fieldName)) {
|
||||
newJsonNode = template.get(fieldName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newJsonNode != null) {
|
||||
// If we are here, it means that the widget being refactored was from a list widget template
|
||||
// Go ahead and refactor this template as well
|
||||
((ObjectNode) newJsonNode).set(FieldName.WIDGET_NAME, new TextNode(newName));
|
||||
// If such a pattern is found, remove that element and attach it back with the new name
|
||||
((ObjectNode) template).remove(fieldName);
|
||||
((ObjectNode) template).set(newName, newJsonNode);
|
||||
// We mark this template path as being a path that was refactored using this boolean value
|
||||
isRefactoredTemplate = true;
|
||||
}
|
||||
}
|
||||
|
||||
final Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
|
||||
// Go through each field to recursively operate on it
|
||||
while (iterator.hasNext()) {
|
||||
final Map.Entry<String, JsonNode> next = iterator.next();
|
||||
final JsonNode value = next.getValue();
|
||||
if (value.isArray()) {
|
||||
// If this field is an array type, iterate through each element and perform replacement
|
||||
final ArrayNode arrayNode = (ArrayNode) value;
|
||||
final ArrayNode newArrayNode = objectMapper.createArrayNode();
|
||||
arrayNode.forEach(x -> newArrayNode.add(replaceStringInJsonNode(x, oldNamePattern, newName)));
|
||||
// Make this array node created from replaced values the new value
|
||||
next.setValue(newArrayNode);
|
||||
} else {
|
||||
// This is either directly a text node or another json node
|
||||
// In either case, recurse over the entire value to get the replaced value
|
||||
next.setValue(replaceStringInJsonNode(value, oldNamePattern, newName));
|
||||
Mono<Set<String>> refactorDynamicBindingsMono = Mono.just(new HashSet<>());
|
||||
Mono<Set<String>> refactorTriggerBindingsMono = Mono.just(new HashSet<>());
|
||||
|
||||
// If there are dynamic bindings in this action configuration, inspect them
|
||||
if (widgetDsl.has(FieldName.DYNAMIC_BINDING_PATH_LIST) && !widgetDsl.get(FieldName.DYNAMIC_BINDING_PATH_LIST).isEmpty()) {
|
||||
ArrayNode dslDynamicBindingPathList = (ArrayNode) widgetDsl.get(FieldName.DYNAMIC_BINDING_PATH_LIST);
|
||||
// recurse over each child
|
||||
refactorDynamicBindingsMono = refactorBindingsUsingBindingPaths(
|
||||
widgetDsl,
|
||||
oldName,
|
||||
newName,
|
||||
evalVersion,
|
||||
oldNamePattern,
|
||||
dslDynamicBindingPathList,
|
||||
widgetName);
|
||||
}
|
||||
|
||||
// If there are dynamic triggers in this action configuration, inspect them
|
||||
if (widgetDsl.has(FieldName.DYNAMIC_TRIGGER_PATH_LIST) && !widgetDsl.get(FieldName.DYNAMIC_TRIGGER_PATH_LIST).isEmpty()) {
|
||||
ArrayNode dslDynamicTriggerPathList = (ArrayNode) widgetDsl.get(FieldName.DYNAMIC_TRIGGER_PATH_LIST);
|
||||
// recurse over each child
|
||||
refactorTriggerBindingsMono = refactorBindingsUsingBindingPaths(
|
||||
widgetDsl,
|
||||
oldName,
|
||||
newName,
|
||||
evalVersion,
|
||||
oldNamePattern,
|
||||
dslDynamicTriggerPathList,
|
||||
widgetName);
|
||||
}
|
||||
|
||||
final String finalWidgetNamePath = widgetName + ".widgetName";
|
||||
final boolean finalIsRefactoredWidget = isRefactoredWidget;
|
||||
final boolean finalIsRefactoredTemplate = isRefactoredTemplate;
|
||||
final String finalWidgetTemplatePath = widgetName + ".template";
|
||||
return refactorDynamicBindingsMono.zipWith(refactorTriggerBindingsMono)
|
||||
.map(tuple -> {
|
||||
tuple.getT1().addAll(tuple.getT2());
|
||||
return tuple.getT1();
|
||||
})
|
||||
.map(refactoredBindings -> {
|
||||
if (Boolean.TRUE.equals(finalIsRefactoredWidget)) {
|
||||
refactoredBindings.add(finalWidgetNamePath);
|
||||
}
|
||||
if (Boolean.TRUE.equals(finalIsRefactoredTemplate)) {
|
||||
refactoredBindings.add(finalWidgetTemplatePath);
|
||||
}
|
||||
return refactoredBindings;
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Mono<Set<String>> refactorBindingsUsingBindingPaths(JsonNode widgetDsl, String oldName, String newName, int evalVersion, Pattern oldNamePattern, ArrayNode bindingPathList, String widgetName) {
|
||||
Mono<Set<String>> refactorBindingsMono;
|
||||
refactorBindingsMono = Flux.fromStream(StreamSupport.stream(bindingPathList.spliterator(), true))
|
||||
.flatMap(bindingPath -> {
|
||||
String key = bindingPath.get(FieldName.KEY).asText();
|
||||
// This is inside a list widget, and the path starts with template.<oldName>,
|
||||
// We need to update the binding path list entry itself as well
|
||||
if (widgetDsl.has(FieldName.WIDGET_TYPE) &&
|
||||
FieldName.LIST_WIDGET.equals(widgetDsl.get(FieldName.WIDGET_TYPE).asText()) &&
|
||||
key.startsWith("template." + oldName)) {
|
||||
key = key.replace(oldName, newName);
|
||||
((ObjectNode) bindingPath).set(FieldName.KEY, new TextNode(key));
|
||||
}
|
||||
// Find values inside mustache bindings in this path
|
||||
Set<String> mustacheValues = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(widgetDsl, key);
|
||||
final String finalKey = key;
|
||||
// Perform refactor for each mustache value
|
||||
return this.replaceValueInMustacheKeys(mustacheValues, oldName, newName, evalVersion, oldNamePattern)
|
||||
.flatMap(replacementMap -> {
|
||||
if (replacementMap.isEmpty()) {
|
||||
// If the map is empty, it means that this path did not have anything that had to be refactored
|
||||
return Mono.empty();
|
||||
}
|
||||
// Replace the binding path value with the new mustache values
|
||||
DslUtils.replaceValuesInSpecificDynamicBindingPath(widgetDsl, finalKey, replacementMap);
|
||||
// Mark this path as refactored
|
||||
String entityPath = StringUtils.hasLength(widgetName) ? widgetName + "." : "";
|
||||
return Mono.just(entityPath + finalKey);
|
||||
});
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
return refactorBindingsMono;
|
||||
}
|
||||
|
||||
Mono<Set<String>> refactorNameInAction(ActionDTO actionDTO, String oldName, String newName,
|
||||
int evalVersion, Pattern oldNamePattern) {
|
||||
// If we're going the fallback route (without AST), we can first filter actions to be refactored
|
||||
// By performing a check on whether json path keys had a reference
|
||||
// This is not needed in the AST way since it would be costlier to make double the number of API calls
|
||||
if (Boolean.FALSE.equals(this.isRtsAccessible)) {
|
||||
Set<String> jsonPathKeys = actionDTO.getJsonPathKeys();
|
||||
|
||||
boolean isReferenceFound = false;
|
||||
if (jsonPathKeys != null && !jsonPathKeys.isEmpty()) {
|
||||
// Since json path keys actually contain the entire inline js function instead of just the widget/action
|
||||
// name, we can not simply use the set.contains(obj) function. We need to iterate over all the keys
|
||||
// in the set and see if the old name is a substring of the json path key.
|
||||
for (String key : jsonPathKeys) {
|
||||
if (oldNamePattern.matcher(key).find()) {
|
||||
isReferenceFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no reference was found, return with an empty set
|
||||
if (Boolean.FALSE.equals(isReferenceFound)) {
|
||||
return Mono.just(new HashSet<>());
|
||||
}
|
||||
}
|
||||
return jsonNode;
|
||||
|
||||
ActionConfiguration actionConfiguration = actionDTO.getActionConfiguration();
|
||||
final JsonNode actionConfigurationNode = objectMapper.convertValue(actionConfiguration, JsonNode.class);
|
||||
|
||||
Mono<Set<String>> refactorDynamicBindingsMono = Mono.just(new HashSet<>());
|
||||
|
||||
// If there are dynamic bindings in this action configuration, inspect them
|
||||
if (actionDTO.getDynamicBindingPathList() != null && !actionDTO.getDynamicBindingPathList().isEmpty()) {
|
||||
// recurse over each child
|
||||
refactorDynamicBindingsMono = Flux.fromIterable(actionDTO.getDynamicBindingPathList())
|
||||
.flatMap(dynamicBindingPath -> {
|
||||
String key = dynamicBindingPath.getKey();
|
||||
Set<String> mustacheValues = new HashSet<>();
|
||||
if (PluginType.JS.equals(actionDTO.getPluginType()) && "body".equals(key)) {
|
||||
mustacheValues.add(actionConfiguration.getBody());
|
||||
|
||||
} else {
|
||||
mustacheValues = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(actionConfigurationNode, key);
|
||||
}
|
||||
return this.replaceValueInMustacheKeys(mustacheValues, oldName, newName, evalVersion, oldNamePattern)
|
||||
.flatMap(replacementMap -> {
|
||||
if (replacementMap.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
DslUtils.replaceValuesInSpecificDynamicBindingPath(actionConfigurationNode, key, replacementMap);
|
||||
String entityPath = StringUtils.hasLength(actionDTO.getValidName()) ? actionDTO.getValidName() + "." : "";
|
||||
return Mono.just(entityPath + key);
|
||||
});
|
||||
})
|
||||
.collect(Collectors.toSet())
|
||||
.map(entityPaths -> {
|
||||
actionDTO.setActionConfiguration(objectMapper.convertValue(actionConfigurationNode, ActionConfiguration.class));
|
||||
return entityPaths;
|
||||
});
|
||||
}
|
||||
|
||||
return refactorDynamicBindingsMono;
|
||||
}
|
||||
|
||||
Mono<Map<String, String>> replaceValueInMustacheKeys(Set<String> mustacheKeySet, String oldName, String
|
||||
newName, int evalVersion, Pattern oldNamePattern) {
|
||||
if (Boolean.TRUE.equals(this.isRtsAccessible)) {
|
||||
return astService.refactorNameInDynamicBindings(mustacheKeySet, oldName, newName, evalVersion);
|
||||
}
|
||||
return this.replaceValueInMustacheKeys(mustacheKeySet, oldNamePattern, newName);
|
||||
}
|
||||
|
||||
Mono<Map<String, String>> replaceValueInMustacheKeys(Set<String> mustacheKeySet, Pattern
|
||||
oldNamePattern, String newName) {
|
||||
return Flux.fromIterable(mustacheKeySet)
|
||||
.flatMap(mustacheKey -> {
|
||||
Matcher matcher = oldNamePattern.matcher(mustacheKey);
|
||||
if (matcher.find()) {
|
||||
return Mono.zip(Mono.just(mustacheKey), Mono.just(matcher.replaceAll(newName)));
|
||||
}
|
||||
return Mono.empty();
|
||||
})
|
||||
.collectMap(Tuple2::getT1, Tuple2::getT2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,6 +301,7 @@ public class ActionCollectionServiceTest {
|
|||
action1.getActionConfiguration().setBody("mockBody");
|
||||
actionCollectionDTO1.setActions(List.of(action1));
|
||||
actionCollectionDTO1.setPluginType(PluginType.JS);
|
||||
actionCollectionDTO1.setBody("export default { x: 1 }");
|
||||
|
||||
final ActionCollectionDTO createdActionCollectionDTO1 = layoutCollectionService.createCollection(actionCollectionDTO1).block();
|
||||
|
||||
|
|
@ -316,7 +317,7 @@ public class ActionCollectionServiceTest {
|
|||
action2.getActionConfiguration().setBody("testCollection1.testAction1()");
|
||||
actionCollectionDTO2.setActions(List.of(action2));
|
||||
actionCollectionDTO2.setPluginType(PluginType.JS);
|
||||
actionCollectionDTO2.setBody("testCollection1.testAction1()");
|
||||
actionCollectionDTO2.setBody("export default { x: testCollection1.testAction1() }");
|
||||
|
||||
final ActionCollectionDTO createdActionCollectionDTO2 = layoutCollectionService.createCollection(actionCollectionDTO2).block();
|
||||
|
||||
|
|
@ -337,7 +338,7 @@ public class ActionCollectionServiceTest {
|
|||
StepVerifier.create(actionCollectionMono)
|
||||
.assertNext(actionCollection -> {
|
||||
assertEquals(
|
||||
"testCollection1.newTestAction1()",
|
||||
"export default { x: testCollection1.newTestAction1() }",
|
||||
actionCollection.getUnpublishedCollection().getBody()
|
||||
);
|
||||
})
|
||||
|
|
@ -379,6 +380,7 @@ public class ActionCollectionServiceTest {
|
|||
action1.getActionConfiguration().setBody("mockBody");
|
||||
actionCollectionDTO1.setActions(List.of(action1));
|
||||
actionCollectionDTO1.setPluginType(PluginType.JS);
|
||||
actionCollectionDTO1.setBody("export default { x: 1 }");
|
||||
|
||||
final ActionCollectionDTO createdActionCollectionDTO1 = layoutCollectionService.createCollection(actionCollectionDTO1).block();
|
||||
|
||||
|
|
@ -394,7 +396,7 @@ public class ActionCollectionServiceTest {
|
|||
action2.getActionConfiguration().setBody("Api1.run()");
|
||||
actionCollectionDTO2.setActions(List.of(action2));
|
||||
actionCollectionDTO2.setPluginType(PluginType.JS);
|
||||
actionCollectionDTO2.setBody("Api1.run()");
|
||||
actionCollectionDTO2.setBody("export default { x: Api1.run() }");
|
||||
|
||||
final ActionCollectionDTO createdActionCollectionDTO2 = layoutCollectionService.createCollection(actionCollectionDTO2).block();
|
||||
|
||||
|
|
@ -415,7 +417,7 @@ public class ActionCollectionServiceTest {
|
|||
StepVerifier.create(actionCollectionMono)
|
||||
.assertNext(actionCollection -> {
|
||||
assertEquals(
|
||||
"Api1.run()",
|
||||
"export default { x: Api1.run() }",
|
||||
actionCollection.getUnpublishedCollection().getBody()
|
||||
);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
package com.appsmith.server.solutions.ce;
|
||||
|
||||
import com.appsmith.server.configurations.InstanceConfig;
|
||||
import com.appsmith.server.helpers.ResponseUtils;
|
||||
import com.appsmith.server.services.ActionCollectionService;
|
||||
import com.appsmith.server.services.ApplicationService;
|
||||
import com.appsmith.server.services.AstService;
|
||||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.NewActionService;
|
||||
import com.appsmith.server.services.NewPageService;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@Slf4j
|
||||
class RefactoringSolutionCEImplTest {
|
||||
|
||||
RefactoringSolutionCEImpl refactoringSolutionCE;
|
||||
@MockBean
|
||||
private ObjectMapper objectMapper;
|
||||
@MockBean
|
||||
private NewPageService newPageService;
|
||||
@MockBean
|
||||
private NewActionService newActionService;
|
||||
@MockBean
|
||||
private ActionCollectionService actionCollectionService;
|
||||
@MockBean
|
||||
private ResponseUtils responseUtils;
|
||||
@MockBean
|
||||
private LayoutActionService layoutActionService;
|
||||
@MockBean
|
||||
private ApplicationService applicationService;
|
||||
@MockBean
|
||||
private AstService astService;
|
||||
@MockBean
|
||||
private InstanceConfig instanceConfig;
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private final String preWord = "\\b(";
|
||||
private final String postWord = ")\\b";
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
refactoringSolutionCE = new RefactoringSolutionCEImpl(objectMapper,
|
||||
newPageService,
|
||||
newActionService,
|
||||
actionCollectionService,
|
||||
responseUtils,
|
||||
layoutActionService,
|
||||
applicationService,
|
||||
astService,
|
||||
instanceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRefactorNameInDsl_whenRenamingTextWidget_replacesAllReferences() {
|
||||
try (InputStream initialStream = this.getClass().getResourceAsStream("refactorDslWithOnlyWidgets.json");
|
||||
InputStream finalStream = this.getClass().getResourceAsStream("refactorDslWithOnlyWidgetsWithNewText.json")) {
|
||||
assert initialStream != null;
|
||||
JsonNode dslAsJsonNode = mapper.readTree(initialStream);
|
||||
final String oldName = "Text3";
|
||||
Mono<Set<String>> updatesMono = refactoringSolutionCE.refactorNameInDsl(
|
||||
dslAsJsonNode,
|
||||
oldName,
|
||||
"newText",
|
||||
2,
|
||||
Pattern.compile(preWord + oldName + postWord));
|
||||
|
||||
StepVerifier.create(updatesMono)
|
||||
.assertNext(updatedPaths -> {
|
||||
Assertions.assertThat(updatedPaths).hasSize(3);
|
||||
Assertions.assertThat(updatedPaths).containsExactlyInAnyOrder(
|
||||
"Text3.widgetName",
|
||||
"List1.template",
|
||||
"List1.onListItemClick");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
JsonNode finalDslAsJsonNode = mapper.readTree(finalStream);
|
||||
Assertions.assertThat(dslAsJsonNode).isEqualTo(finalDslAsJsonNode);
|
||||
|
||||
} catch (IOException e) {
|
||||
Assertions.fail("Unexpected IOException", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRefactorNameInDsl_whenRenamingListWidget_replacesTemplateReferences() {
|
||||
try (InputStream initialStream = this.getClass().getResourceAsStream("refactorDslWithOnlyWidgets.json");
|
||||
InputStream finalStream = this.getClass().getResourceAsStream("refactorDslWithOnlyWidgetsWithNewList.json")) {
|
||||
assert initialStream != null;
|
||||
JsonNode dslAsJsonNode = mapper.readTree(initialStream);
|
||||
final String oldName = "List1";
|
||||
Mono<Set<String>> updatesMono = refactoringSolutionCE.refactorNameInDsl(
|
||||
dslAsJsonNode,
|
||||
oldName,
|
||||
"newList",
|
||||
2,
|
||||
Pattern.compile(preWord + oldName + postWord));
|
||||
|
||||
StepVerifier.create(updatesMono)
|
||||
.assertNext(updatedPaths -> {
|
||||
Assertions.assertThat(updatedPaths).hasSize(4);
|
||||
Assertions.assertThat(updatedPaths).containsExactlyInAnyOrder(
|
||||
"List1.widgetName",
|
||||
"List1.template.Text4.text",
|
||||
"List1.template.Image1.image",
|
||||
"List1.template.Text3.text");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
JsonNode finalDslAsJsonNode = mapper.readTree(finalStream);
|
||||
Assertions.assertThat(dslAsJsonNode).isEqualTo(finalDslAsJsonNode);
|
||||
|
||||
} catch (IOException e) {
|
||||
Assertions.fail("Unexpected IOException", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.models.ActionConfiguration;
|
|||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.external.models.Datasource;
|
||||
import com.appsmith.external.models.PluginType;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.ActionCollection;
|
||||
import com.appsmith.server.domains.Application;
|
||||
|
|
@ -247,9 +248,12 @@ class RefactoringSolutionCETest {
|
|||
action.setDatasource(datasource);
|
||||
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "firstWidgetId");
|
||||
dsl.put("widgetName", "firstWidget");
|
||||
JSONArray temp = new JSONArray();
|
||||
temp.addAll(List.of(new JSONObject(Map.of("key", "testField"))));
|
||||
temp.addAll(List.of(new JSONObject(Map.of("key", "innerArrayReference[0].innerK")),
|
||||
new JSONObject(Map.of("key", "innerObjectReference.k")),
|
||||
new JSONObject(Map.of("key", "testField"))));
|
||||
dsl.put("dynamicBindingPathList", temp);
|
||||
dsl.put("testField", "{{ \tbeforeNameChange.data }}");
|
||||
final JSONObject innerObjectReference = new JSONObject();
|
||||
|
|
@ -311,9 +315,12 @@ class RefactoringSolutionCETest {
|
|||
action.setDatasource(datasource);
|
||||
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "firstWidgetId");
|
||||
dsl.put("widgetName", "firstWidget");
|
||||
JSONArray temp = new JSONArray();
|
||||
temp.addAll(List.of(new JSONObject(Map.of("key", "testField"))));
|
||||
temp.addAll(List.of(new JSONObject(Map.of("key", "innerArrayReference[0].innerK")),
|
||||
new JSONObject(Map.of("key", "innerObjectReference.k")),
|
||||
new JSONObject(Map.of("key", "testField"))));
|
||||
dsl.put("dynamicBindingPathList", temp);
|
||||
dsl.put("testField", "{{ \tbeforeNameChange.data }}");
|
||||
final JSONObject innerObjectReference = new JSONObject();
|
||||
|
|
@ -477,6 +484,7 @@ class RefactoringSolutionCETest {
|
|||
action.setDatasource(datasource);
|
||||
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "firstWidgetId");
|
||||
dsl.put("widgetName", "firstWidget");
|
||||
JSONArray temp = new JSONArray();
|
||||
temp.addAll(List.of(new JSONObject(Map.of("key", "testField"))));
|
||||
|
|
@ -545,6 +553,7 @@ class RefactoringSolutionCETest {
|
|||
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor()));
|
||||
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "testId");
|
||||
dsl.put("widgetName", "Table1");
|
||||
dsl.put("type", "TABLE_WIDGET");
|
||||
Map primaryColumns = new HashMap<String, Object>();
|
||||
|
|
@ -591,6 +600,7 @@ class RefactoringSolutionCETest {
|
|||
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor()));
|
||||
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "simpleRefactorId");
|
||||
dsl.put("widgetName", "Table1");
|
||||
dsl.put("type", "TABLE_WIDGET");
|
||||
Layout layout = testPage.getLayouts().get(0);
|
||||
|
|
@ -626,13 +636,17 @@ class RefactoringSolutionCETest {
|
|||
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor()));
|
||||
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "testId");
|
||||
dsl.put("widgetName", "List1");
|
||||
dsl.put("type", "LIST_WIDGET");
|
||||
JSONObject template = new JSONObject();
|
||||
template.put("oldWidgetName", "irrelevantContent");
|
||||
JSONObject oldWidgetTemplate = new JSONObject();
|
||||
oldWidgetTemplate.put("widgetName", "oldWidgetName");
|
||||
template.put("oldWidgetName", oldWidgetTemplate);
|
||||
dsl.put("template", template);
|
||||
final JSONArray children = new JSONArray();
|
||||
final JSONObject defaultWidget = new JSONObject();
|
||||
defaultWidget.put("widgetId", "testId2");
|
||||
defaultWidget.put("widgetName", "oldWidgetName");
|
||||
defaultWidget.put("type", "TEXT_WIDGET");
|
||||
children.add(defaultWidget);
|
||||
|
|
@ -648,7 +662,7 @@ class RefactoringSolutionCETest {
|
|||
refactorNameDTO.setOldName("oldWidgetName");
|
||||
refactorNameDTO.setNewName("newWidgetName");
|
||||
|
||||
Mono<LayoutDTO> widgetRenameMono = refactoringSolution.refactorWidgetName(refactorNameDTO).cache();
|
||||
Mono<LayoutDTO> widgetRenameMono = refactoringSolution.refactorWidgetName(refactorNameDTO);
|
||||
|
||||
StepVerifier
|
||||
.create(widgetRenameMono)
|
||||
|
|
@ -667,6 +681,7 @@ class RefactoringSolutionCETest {
|
|||
|
||||
// Set up table widget in DSL
|
||||
JSONObject dsl = new JSONObject();
|
||||
dsl.put("widgetId", "testId");
|
||||
dsl.put("widgetName", "Table1");
|
||||
dsl.put("type", "TABLE_WIDGET");
|
||||
Layout layout = testPage.getLayouts().get(0);
|
||||
|
|
@ -681,11 +696,16 @@ class RefactoringSolutionCETest {
|
|||
actionCollectionDTO1.setApplicationId(testApp.getId());
|
||||
actionCollectionDTO1.setWorkspaceId(testApp.getWorkspaceId());
|
||||
actionCollectionDTO1.setPluginId(jsDatasource.getPluginId());
|
||||
|
||||
ActionDTO action1 = new ActionDTO();
|
||||
action1.setName("testAction1");
|
||||
action1.setActionConfiguration(new ActionConfiguration());
|
||||
action1.getActionConfiguration().setBody("\tTable1");
|
||||
actionCollectionDTO1.setBody("\tTable1");
|
||||
action1.setDynamicBindingPathList(List.of(new Property("body", null)));
|
||||
action1.setPluginType(PluginType.JS);
|
||||
|
||||
actionCollectionDTO1.setActions(List.of(action1));
|
||||
actionCollectionDTO1.setBody("export default { x : \tTable1 }");
|
||||
actionCollectionDTO1.setActions(List.of(action1));
|
||||
actionCollectionDTO1.setPluginType(PluginType.JS);
|
||||
|
||||
|
|
@ -710,7 +730,7 @@ class RefactoringSolutionCETest {
|
|||
.assertNext(tuple -> {
|
||||
final ActionCollection actionCollection = tuple.getT1();
|
||||
final NewAction action = tuple.getT2();
|
||||
assertThat(actionCollection.getUnpublishedCollection().getBody()).isEqualTo("\tNewNameTable1");
|
||||
assertThat(actionCollection.getUnpublishedCollection().getBody()).isEqualTo("export default { x : \tNewNameTable1 }");
|
||||
final ActionDTO unpublishedAction = action.getUnpublishedAction();
|
||||
assertThat(unpublishedAction.getJsonPathKeys().size()).isEqualTo(1);
|
||||
final Optional<String> first = unpublishedAction.getJsonPathKeys().stream().findFirst();
|
||||
|
|
@ -734,6 +754,7 @@ class RefactoringSolutionCETest {
|
|||
originalActionCollectionDTO.setPageId(testPage.getId());
|
||||
originalActionCollectionDTO.setPluginId(jsDatasource.getPluginId());
|
||||
originalActionCollectionDTO.setPluginType(PluginType.JS);
|
||||
originalActionCollectionDTO.setBody("export default { x: 1 }");
|
||||
|
||||
ActionDTO action1 = new ActionDTO();
|
||||
action1.setName("testAction1");
|
||||
|
|
@ -747,7 +768,7 @@ class RefactoringSolutionCETest {
|
|||
ActionCollectionDTO actionCollectionDTO = new ActionCollectionDTO();
|
||||
assert dto != null;
|
||||
actionCollectionDTO.setId(dto.getId());
|
||||
actionCollectionDTO.setBody("body");
|
||||
actionCollectionDTO.setBody("export default { x: Table1 }");
|
||||
actionCollectionDTO.setName("newName");
|
||||
|
||||
RefactorActionNameInCollectionDTO refactorActionNameInCollectionDTO = new RefactorActionNameInCollectionDTO();
|
||||
|
|
@ -772,7 +793,7 @@ class RefactoringSolutionCETest {
|
|||
final ActionCollectionDTO actionCollectionDTOResult = tuple.getT1().getUnpublishedCollection();
|
||||
final NewAction newAction = tuple.getT2();
|
||||
assertEquals("originalName", actionCollectionDTOResult.getName());
|
||||
assertEquals("body", actionCollectionDTOResult.getBody());
|
||||
assertEquals("export default { x: Table1 }", actionCollectionDTOResult.getBody());
|
||||
assertEquals("newTestAction", newAction.getUnpublishedAction().getName());
|
||||
assertEquals("originalName.newTestAction", newAction.getUnpublishedAction().getFullyQualifiedName());
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,374 @@
|
|||
{
|
||||
"widgetName": "MainContainer",
|
||||
"widgetId": "0",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Text1",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "Label",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "wemfst2t7m",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text2",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "text"
|
||||
}
|
||||
],
|
||||
"text": "{{Text1.text}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "2bensj901c",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"template": {
|
||||
"Image1": {
|
||||
"image": "{{List1.listData.map((currentItem) => currentItem.img)}}",
|
||||
"widgetName": "Image1",
|
||||
"type": "IMAGE_WIDGET",
|
||||
"key": "e0c7wcn17q",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "image"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "bvixbymoxr",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"Text3": {
|
||||
"text": "{{List1.listData.map((currentItem) => currentItem.name)}}",
|
||||
"widgetName": "Text3",
|
||||
"type": "TEXT_WIDGET",
|
||||
"key": "3pqpn28ba4",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "6ox4ujv63y",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"Text4": {
|
||||
"text": "{{List1.listData.map((currentItem, currentIndex) => {\n return (function(){\n return currentItem.id + Text1.text;\n })();\n })}}",
|
||||
"widgetName": "Text4",
|
||||
"type": "TEXT_WIDGET",
|
||||
"key": "3pqpn28ba4",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "rtlyvpkvhc",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
},
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "List1",
|
||||
"type": "LIST_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "template.Image1.image"
|
||||
},
|
||||
{
|
||||
"key": "template.Text3.text"
|
||||
},
|
||||
{
|
||||
"key": "template.Text4.text"
|
||||
}
|
||||
],
|
||||
"dynamicTriggerPathList": [
|
||||
{
|
||||
"key": "onListItemClick"
|
||||
}
|
||||
],
|
||||
"onListItemClick": "{{Text3.text}}",
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas1",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "Container1",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas2",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Image1",
|
||||
"type": "IMAGE_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "image"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"key": "e0c7wcn17q",
|
||||
"image": "{{currentItem.img}}",
|
||||
"widgetId": "bvixbymoxr",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text3",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "{{currentItem.name}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "6ox4ujv63y",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text4",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "{{currentItem.id + Text1.text}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "rtlyvpkvhc",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "3m0y9rrh1o",
|
||||
"widgetId": "zdz4f503fm",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "sca9shlkpb",
|
||||
"widgetId": "vt8i2g9u5r",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "3m0y9rrh1o",
|
||||
"widgetId": "ki75z4pfxm",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "t35n4gddpu",
|
||||
"widgetId": "bunz1f076j",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"type": "TABLE_WIDGET_V2",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "primaryColumns.step.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.task.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.status.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.button.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.button.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.menuButton.menuColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.menuButton.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.iconButton.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.iconButton.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.saveButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.saveBorderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.discardButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.discardBorderRadius"
|
||||
}
|
||||
],
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"childStylesheet": {
|
||||
"button": {
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"menuButton": {
|
||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"iconButton": {
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"editActions": {
|
||||
"saveButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"saveBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"discardButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"discardBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
},
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"widgetName": "Table1",
|
||||
"primaryColumns": {
|
||||
"step": {
|
||||
"id": "step",
|
||||
"originalId": "step",
|
||||
"alias": "step",
|
||||
"label": "step",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"step\"]))}}"
|
||||
},
|
||||
"task": {
|
||||
"id": "task",
|
||||
"originalId": "task",
|
||||
"alias": "task",
|
||||
"label": "task",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( Text1.text + \" \" + currentRow[\"task\"]))}}"
|
||||
},
|
||||
"status": {
|
||||
"id": "status",
|
||||
"originalId": "status",
|
||||
"alias": "status",
|
||||
"label": "status",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"status\"]))}}"
|
||||
},
|
||||
"action": {
|
||||
"id": "action",
|
||||
"originalId": "action",
|
||||
"alias": "action",
|
||||
"label": "action",
|
||||
"onClick": "{{currentRow.step === '#1' ? showAlert('Done', 'success') : currentRow.step === '#2' ? navigateTo('https://docs.appsmith.com/core-concepts/connecting-to-data-sources/querying-a-database',undefined,'NEW_WINDOW') : navigateTo('https://docs.appsmith.com/core-concepts/displaying-data-read/display-data-tables',undefined,'NEW_WINDOW')}}",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"action\"]))}}",
|
||||
"buttonColor": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.colors.primaryColor))}}",
|
||||
"borderRadius": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.borderRadius.appBorderRadius))}}",
|
||||
"boxShadow": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( 'none'))}}"
|
||||
}
|
||||
},
|
||||
"key": "ouqfcjyuwa",
|
||||
"widgetId": "vrcp6kbiz8"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
{
|
||||
"widgetName": "MainContainer",
|
||||
"widgetId": "0",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Text1",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "Label",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "wemfst2t7m",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text2",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "text"
|
||||
}
|
||||
],
|
||||
"text": "{{Text1.text}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "2bensj901c",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"template": {
|
||||
"Image1": {
|
||||
"image": "{{newList.listData.map((currentItem) => currentItem.img)}}",
|
||||
"widgetName": "Image1",
|
||||
"type": "IMAGE_WIDGET",
|
||||
"key": "e0c7wcn17q",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "image"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "bvixbymoxr",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"Text3": {
|
||||
"text": "{{newList.listData.map((currentItem) => currentItem.name)}}",
|
||||
"widgetName": "Text3",
|
||||
"type": "TEXT_WIDGET",
|
||||
"key": "3pqpn28ba4",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "6ox4ujv63y",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"Text4": {
|
||||
"text": "{{newList.listData.map((currentItem, currentIndex) => {\n return (function(){\n return currentItem.id + Text1.text;\n })();\n })}}",
|
||||
"widgetName": "Text4",
|
||||
"type": "TEXT_WIDGET",
|
||||
"key": "3pqpn28ba4",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "rtlyvpkvhc",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
},
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "newList",
|
||||
"type": "LIST_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "template.Image1.image"
|
||||
},
|
||||
{
|
||||
"key": "template.Text3.text"
|
||||
},
|
||||
{
|
||||
"key": "template.Text4.text"
|
||||
}
|
||||
],
|
||||
"dynamicTriggerPathList": [
|
||||
{
|
||||
"key": "onListItemClick"
|
||||
}
|
||||
],
|
||||
"onListItemClick": "{{Text3.text}}",
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas1",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "Container1",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas2",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Image1",
|
||||
"type": "IMAGE_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "image"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"key": "e0c7wcn17q",
|
||||
"image": "{{currentItem.img}}",
|
||||
"widgetId": "bvixbymoxr",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text3",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "{{currentItem.name}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "6ox4ujv63y",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text4",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "{{currentItem.id + Text1.text}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "rtlyvpkvhc",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "3m0y9rrh1o",
|
||||
"widgetId": "zdz4f503fm",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "sca9shlkpb",
|
||||
"widgetId": "vt8i2g9u5r",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "3m0y9rrh1o",
|
||||
"widgetId": "ki75z4pfxm",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "t35n4gddpu",
|
||||
"widgetId": "bunz1f076j",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"type": "TABLE_WIDGET_V2",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "primaryColumns.step.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.task.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.status.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.button.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.button.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.menuButton.menuColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.menuButton.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.iconButton.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.iconButton.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.saveButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.saveBorderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.discardButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.discardBorderRadius"
|
||||
}
|
||||
],
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"childStylesheet": {
|
||||
"button": {
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"menuButton": {
|
||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"iconButton": {
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"editActions": {
|
||||
"saveButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"saveBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"discardButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"discardBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
},
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"widgetName": "Table1",
|
||||
"primaryColumns": {
|
||||
"step": {
|
||||
"id": "step",
|
||||
"originalId": "step",
|
||||
"alias": "step",
|
||||
"label": "step",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"step\"]))}}"
|
||||
},
|
||||
"task": {
|
||||
"id": "task",
|
||||
"originalId": "task",
|
||||
"alias": "task",
|
||||
"label": "task",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( Text1.text + \" \" + currentRow[\"task\"]))}}"
|
||||
},
|
||||
"status": {
|
||||
"id": "status",
|
||||
"originalId": "status",
|
||||
"alias": "status",
|
||||
"label": "status",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"status\"]))}}"
|
||||
},
|
||||
"action": {
|
||||
"id": "action",
|
||||
"originalId": "action",
|
||||
"alias": "action",
|
||||
"label": "action",
|
||||
"onClick": "{{currentRow.step === '#1' ? showAlert('Done', 'success') : currentRow.step === '#2' ? navigateTo('https://docs.appsmith.com/core-concepts/connecting-to-data-sources/querying-a-database',undefined,'NEW_WINDOW') : navigateTo('https://docs.appsmith.com/core-concepts/displaying-data-read/display-data-tables',undefined,'NEW_WINDOW')}}",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"action\"]))}}",
|
||||
"buttonColor": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.colors.primaryColor))}}",
|
||||
"borderRadius": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.borderRadius.appBorderRadius))}}",
|
||||
"boxShadow": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( 'none'))}}"
|
||||
}
|
||||
},
|
||||
"key": "ouqfcjyuwa",
|
||||
"widgetId": "vrcp6kbiz8"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
{
|
||||
"widgetName": "MainContainer",
|
||||
"widgetId": "0",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Text1",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "Label",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "wemfst2t7m",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text2",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "text"
|
||||
}
|
||||
],
|
||||
"text": "{{Text1.text}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "2bensj901c",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"template": {
|
||||
"Image1": {
|
||||
"image": "{{List1.listData.map((currentItem) => currentItem.img)}}",
|
||||
"widgetName": "Image1",
|
||||
"type": "IMAGE_WIDGET",
|
||||
"key": "e0c7wcn17q",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "image"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "bvixbymoxr",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"newText": {
|
||||
"text": "{{List1.listData.map((currentItem) => currentItem.name)}}",
|
||||
"widgetName": "newText",
|
||||
"type": "TEXT_WIDGET",
|
||||
"key": "3pqpn28ba4",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "6ox4ujv63y",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"Text4": {
|
||||
"text": "{{List1.listData.map((currentItem, currentIndex) => {\n return (function(){\n return currentItem.id + Text1.text;\n })();\n })}}",
|
||||
"widgetName": "Text4",
|
||||
"type": "TEXT_WIDGET",
|
||||
"key": "3pqpn28ba4",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"widgetId": "rtlyvpkvhc",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
},
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "List1",
|
||||
"type": "LIST_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "template.Image1.image"
|
||||
},
|
||||
{
|
||||
"key": "template.newText.text"
|
||||
},
|
||||
{
|
||||
"key": "template.Text4.text"
|
||||
}
|
||||
],
|
||||
"dynamicTriggerPathList": [
|
||||
{
|
||||
"key": "onListItemClick"
|
||||
}
|
||||
],
|
||||
"onListItemClick": "{{newText.text}}",
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas1",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "Container1",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas2",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Image1",
|
||||
"type": "IMAGE_WIDGET",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "image"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"key": "e0c7wcn17q",
|
||||
"image": "{{currentItem.img}}",
|
||||
"widgetId": "bvixbymoxr",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "newText",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "{{currentItem.name}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "6ox4ujv63y",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"widgetName": "Text4",
|
||||
"type": "TEXT_WIDGET",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "text"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "{{currentItem.id + Text1.text}}",
|
||||
"key": "3pqpn28ba4",
|
||||
"widgetId": "rtlyvpkvhc",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "3m0y9rrh1o",
|
||||
"widgetId": "zdz4f503fm",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "sca9shlkpb",
|
||||
"widgetId": "vt8i2g9u5r",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "3m0y9rrh1o",
|
||||
"widgetId": "ki75z4pfxm",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
],
|
||||
"key": "t35n4gddpu",
|
||||
"widgetId": "bunz1f076j",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"type": "TABLE_WIDGET_V2",
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "primaryColumns.step.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.task.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.status.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.computedValue"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "primaryColumns.action.boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.button.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.button.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.menuButton.menuColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.menuButton.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.iconButton.buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.iconButton.borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.saveButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.saveBorderRadius"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.discardButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "childStylesheet.editActions.discardBorderRadius"
|
||||
}
|
||||
],
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"childStylesheet": {
|
||||
"button": {
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"menuButton": {
|
||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"iconButton": {
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
},
|
||||
"editActions": {
|
||||
"saveButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"saveBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"discardButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"discardBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
},
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"widgetName": "Table1",
|
||||
"primaryColumns": {
|
||||
"step": {
|
||||
"id": "step",
|
||||
"originalId": "step",
|
||||
"alias": "step",
|
||||
"label": "step",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"step\"]))}}"
|
||||
},
|
||||
"task": {
|
||||
"id": "task",
|
||||
"originalId": "task",
|
||||
"alias": "task",
|
||||
"label": "task",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( Text1.text + \" \" + currentRow[\"task\"]))}}"
|
||||
},
|
||||
"status": {
|
||||
"id": "status",
|
||||
"originalId": "status",
|
||||
"alias": "status",
|
||||
"label": "status",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"status\"]))}}"
|
||||
},
|
||||
"action": {
|
||||
"id": "action",
|
||||
"originalId": "action",
|
||||
"alias": "action",
|
||||
"label": "action",
|
||||
"onClick": "{{currentRow.step === '#1' ? showAlert('Done', 'success') : currentRow.step === '#2' ? navigateTo('https://docs.appsmith.com/core-concepts/connecting-to-data-sources/querying-a-database',undefined,'NEW_WINDOW') : navigateTo('https://docs.appsmith.com/core-concepts/displaying-data-read/display-data-tables',undefined,'NEW_WINDOW')}}",
|
||||
"computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"action\"]))}}",
|
||||
"buttonColor": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.colors.primaryColor))}}",
|
||||
"borderRadius": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.borderRadius.appBorderRadius))}}",
|
||||
"boxShadow": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( 'none'))}}"
|
||||
}
|
||||
},
|
||||
"key": "ouqfcjyuwa",
|
||||
"widgetId": "vrcp6kbiz8"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { ancestor, simple } from "acorn-walk";
|
|||
import { ECMA_VERSION, NodeTypes } from "./constants/ast";
|
||||
import { has, isFinite, isString, memoize, toPath } from "lodash";
|
||||
import { isTrueObject, sanitizeScript } from "./utils";
|
||||
|
||||
import { jsObjectDeclaration } from "./jsObject/index";
|
||||
/*
|
||||
* Valuable links:
|
||||
*
|
||||
|
|
@ -107,6 +107,11 @@ type NodeWithLocation<NodeType> = NodeType & {
|
|||
|
||||
type AstOptions = Omit<Options, "ecmaVersion">;
|
||||
|
||||
type EntityRefactorResponse = {
|
||||
isSuccess: boolean;
|
||||
body: { script: string; refactorCount: number } | { error: string };
|
||||
};
|
||||
|
||||
/* We need these functions to typescript casts the nodes with the correct types */
|
||||
export const isIdentifierNode = (node: Node): node is IdentifierNode => {
|
||||
return node.type === NodeTypes.Identifier;
|
||||
|
|
@ -213,7 +218,6 @@ export const extractIdentifierInfoFromCode = (
|
|||
evaluationVersion: number,
|
||||
invalidIdentifiers?: Record<string, unknown>
|
||||
): IdentifierInfo => {
|
||||
|
||||
let ast: Node = { end: 0, start: 0, type: "" };
|
||||
try {
|
||||
const sanitizedScript = sanitizeScript(code, evaluationVersion);
|
||||
|
|
@ -262,9 +266,12 @@ export const entityRefactorFromCode = (
|
|||
script: string,
|
||||
oldName: string,
|
||||
newName: string,
|
||||
isJSObject: boolean,
|
||||
evaluationVersion: number,
|
||||
invalidIdentifiers?: Record<string, unknown>
|
||||
): Record<string, string | number> | string => {
|
||||
): EntityRefactorResponse => {
|
||||
//If script is a JSObject then replace export default to decalartion.
|
||||
if (isJSObject) script = jsObjectToCode(script);
|
||||
let ast: Node = { end: 0, start: 0, type: "" };
|
||||
//Copy of script to refactor
|
||||
let refactorScript = script;
|
||||
|
|
@ -284,7 +291,7 @@ export const entityRefactorFromCode = (
|
|||
identifierList,
|
||||
}: NodeList = ancestorWalk(ast);
|
||||
let identifierArray = Array.from(identifierList) as Array<IdentifierNode>;
|
||||
const referencesArr = Array.from(references).filter((reference, index) => {
|
||||
Array.from(references).forEach((reference, index) => {
|
||||
const topLevelIdentifier = toPath(reference)[0];
|
||||
let shouldUpdateNode = !(
|
||||
functionalParams.has(topLevelIdentifier) ||
|
||||
|
|
@ -308,13 +315,17 @@ export const entityRefactorFromCode = (
|
|||
refactorOffset += nameLengthDiff;
|
||||
++refactorCount;
|
||||
}
|
||||
return shouldUpdateNode;
|
||||
});
|
||||
return { script: refactorScript, count: refactorCount };
|
||||
//If script is a JSObject then revert decalartion to export default.
|
||||
if (isJSObject) refactorScript = jsCodeToObject(refactorScript);
|
||||
return {
|
||||
isSuccess: true,
|
||||
body: { script: refactorScript, refactorCount },
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// Syntax error. Ignore and return empty list
|
||||
return "Syntax Error";
|
||||
return { isSuccess: false, body: { error: "Syntax Error" } };
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -598,3 +609,15 @@ const ancestorWalk = (ast: Node): NodeList => {
|
|||
identifierList,
|
||||
};
|
||||
};
|
||||
|
||||
//Replace export default by a variable declaration.
|
||||
//This is required for acorn to parse code into AST.
|
||||
const jsObjectToCode = (script: string) => {
|
||||
return script.replace(/export default/g, jsObjectDeclaration);
|
||||
};
|
||||
|
||||
//Revert the string replacement from 'jsObjectToCode'.
|
||||
//variable declaration is replaced back by export default.
|
||||
const jsCodeToObject = (script: string) => {
|
||||
return script.replace(jsObjectDeclaration, "export default");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Node } from 'acorn';
|
||||
import { getAST } from '../index';
|
||||
import { generate } from 'astring';
|
||||
import { simple } from 'acorn-walk';
|
||||
import { Node } from "acorn";
|
||||
import { getAST } from "../index";
|
||||
import { generate } from "astring";
|
||||
import { simple } from "acorn-walk";
|
||||
import {
|
||||
getFunctionalParamsFromNode,
|
||||
isPropertyAFunctionNode,
|
||||
|
|
@ -9,7 +9,7 @@ import {
|
|||
isObjectExpression,
|
||||
PropertyNode,
|
||||
functionParam,
|
||||
} from '../index';
|
||||
} from "../index";
|
||||
|
||||
type JsObjectProperty = {
|
||||
key: string;
|
||||
|
|
@ -18,6 +18,11 @@ type JsObjectProperty = {
|
|||
arguments?: Array<functionParam>;
|
||||
};
|
||||
|
||||
const jsObjectVariableName =
|
||||
"____INTERNAL_JS_OBJECT_NAME_USED_FOR_PARSING_____";
|
||||
|
||||
export const jsObjectDeclaration = `var ${jsObjectVariableName} =`;
|
||||
|
||||
export const parseJSObjectWithAST = (
|
||||
jsObjectBody: string
|
||||
): Array<JsObjectProperty> => {
|
||||
|
|
@ -26,9 +31,7 @@ export const parseJSObjectWithAST = (
|
|||
if the variable name will be same then also we won't have problem here as jsObjectVariableName will be last node in VariableDeclarator hence overriding the previous JSObjectProperties.
|
||||
Keeping this just for sanity check if any caveat was missed.
|
||||
*/
|
||||
const jsObjectVariableName =
|
||||
'____INTERNAL_JS_OBJECT_NAME_USED_FOR_PARSING_____';
|
||||
const jsCode = `var ${jsObjectVariableName} = ${jsObjectBody}`;
|
||||
const jsCode = `${jsObjectDeclaration} ${jsObjectBody}`;
|
||||
|
||||
const ast = getAST(jsCode);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user