fix: Refactor entities based on AST parsing logic (#18517)
* feat: Refactor entities based on AST parsing logic * Deleted jmh file * rts updates for the edge cases * adding jest test cases * update review comments * Fixed issue with references outside of bindings and some other stuff * Added tests for DSLUtils * bug fix 18699 * Test fixes * Test fixes * Review comments * Changed type to boxed Co-authored-by: ChandanBalajiBP <chandan@appsmith.com> Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com>
This commit is contained in:
parent
284571803b
commit
5329010415
|
|
@ -36,7 +36,6 @@
|
|||
"dependencies": {
|
||||
"express-validator": "^6.14.2",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"morgan": "^1.10.0",
|
||||
"supertest": "^6.2.4",
|
||||
"tsc-alias": "^1.7.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import http from "http";
|
||||
import path from "path";
|
||||
import express from "express";
|
||||
import morgan from "morgan";
|
||||
import { Server } from "socket.io";
|
||||
import log, { LogLevelDesc } from "loglevel";
|
||||
import { VERSION as buildVersion } from "./version"; // release version of the api
|
||||
|
|
@ -49,8 +48,6 @@ const io = new Server(server, {
|
|||
// Initializing Sockets
|
||||
initializeSockets(io);
|
||||
|
||||
//Track perf metrics for each call
|
||||
app.use(morgan('tiny'));
|
||||
// parse incoming json requests
|
||||
app.use(express.json({ limit: "5mb" }));
|
||||
// Initializing Routes
|
||||
|
|
|
|||
|
|
@ -50,6 +50,44 @@ const entityRefactor = [
|
|||
isJSObject: true,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
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.data",
|
||||
newName: "ApiNever.input",
|
||||
isJSObject: false,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
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.dat",
|
||||
newName: "ApiNever.input",
|
||||
isJSObject: false,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
script: "\tApiNever.data",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: false,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
script: "ApiNever.data + ApiNever.data",
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: false,
|
||||
evalVersion: 2,
|
||||
},
|
||||
{
|
||||
script:
|
||||
'export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t\t// ApiNever.text\n\t\treturn "ApiNever.text" + ApiNever.text\n\t},\n\tmyFun2: async () => {\n\t\t//use async-await or promises\n\t\t// ApiNever.text\n\t\treturn "ApiNever.text" + ApiNever.text\n\t}\n}',
|
||||
oldName: "ApiNever",
|
||||
newName: "ApiForever",
|
||||
isJSObject: true,
|
||||
evalVersion: 2,
|
||||
},
|
||||
];
|
||||
|
||||
afterAll((done) => {
|
||||
|
|
@ -124,6 +162,29 @@ describe("AST tests", () => {
|
|||
"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,
|
||||
},
|
||||
{
|
||||
script:
|
||||
"// ApiNever \n function ApiNever(abc) {let foo = \"I'm getting data from ApiNever but don't rename this string\" + ApiNever.input; \n if(true) { return ApiNever }}",
|
||||
refactorCount: 1,
|
||||
},
|
||||
{
|
||||
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 }}",
|
||||
refactorCount: 0,
|
||||
},
|
||||
{
|
||||
script: "\tApiForever.data",
|
||||
refactorCount: 1,
|
||||
},
|
||||
{
|
||||
script: "ApiForever.data + ApiForever.data",
|
||||
refactorCount: 2,
|
||||
},
|
||||
{
|
||||
script:
|
||||
'export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t\t// ApiNever.text\n\t\treturn "ApiNever.text" + ApiForever.text\n\t},\n\tmyFun2: async () => {\n\t\t//use async-await or promises\n\t\t// ApiNever.text\n\t\treturn "ApiNever.text" + ApiForever.text\n\t}\n}',
|
||||
refactorCount: 2,
|
||||
},
|
||||
];
|
||||
|
||||
await supertest(app)
|
||||
|
|
|
|||
|
|
@ -951,13 +951,6 @@ base64id@2.0.0, base64id@~2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
|
||||
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
|
||||
|
||||
basic-auth@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
|
||||
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
|
||||
dependencies:
|
||||
safe-buffer "5.1.2"
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
|
|
@ -1283,7 +1276,7 @@ denque@^1.4.1:
|
|||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de"
|
||||
integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==
|
||||
|
||||
depd@2.0.0, depd@~2.0.0:
|
||||
depd@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
|
@ -2475,17 +2468,6 @@ mongodb@^3.6.4:
|
|||
optionalDependencies:
|
||||
saslprep "^1.0.0"
|
||||
|
||||
morgan@^1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
|
||||
integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
|
||||
dependencies:
|
||||
basic-auth "~2.0.1"
|
||||
debug "2.6.9"
|
||||
depd "~2.0.0"
|
||||
on-finished "~2.3.0"
|
||||
on-headers "~1.0.2"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
|
@ -2560,18 +2542,6 @@ on-finished@2.4.1:
|
|||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
on-finished@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
on-headers@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
|
||||
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
|
||||
|
||||
once@1.4.0, once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
|
|
@ -2857,16 +2827,16 @@ run-parallel@^1.1.9:
|
|||
dependencies:
|
||||
queue-microtask "^1.2.2"
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@5.2.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ public enum AnalyticsEvents {
|
|||
// Events to log execution time
|
||||
GIT_SERIALIZE_APP_RESOURCES_TO_LOCAL_FILE,
|
||||
GIT_DESERIALIZE_APP_RESOURCES_FROM_FILE,
|
||||
|
||||
// Entity refactor related events
|
||||
REFACTOR_JSOBJECT,
|
||||
REFACTOR_ACTION,
|
||||
REFACTOR_JSACTION,
|
||||
REFACTOR_WIDGET,
|
||||
|
||||
INVITE_USERS_TO_USER_GROUPS,
|
||||
REMOVE_USERS_FROM_USER_GROUPS,
|
||||
ASSIGNED_TO_PERMISSION_GROUP,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.appsmith.external.helpers;
|
|||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.EntityDependencyNode;
|
||||
import com.appsmith.external.models.EntityReferenceType;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
|
|
@ -23,7 +24,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -79,12 +79,12 @@ public class MustacheHelper {
|
|||
* should give the original template back. The tokens are split such that alternative strings in the list are plain
|
||||
* text and the others are mustache interpolations.
|
||||
*/
|
||||
public static List<String> tokenize(String template) {
|
||||
public static List<MustacheBindingToken> tokenize(String template) {
|
||||
if (!StringUtils.hasLength(template)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> tokens = new ArrayList<>();
|
||||
List<MustacheBindingToken> tokens = new ArrayList<>();
|
||||
|
||||
int length = template.length();
|
||||
|
||||
|
|
@ -100,6 +100,7 @@ public class MustacheHelper {
|
|||
int braceDepth = 0;
|
||||
|
||||
StringBuilder currentToken = new StringBuilder().append(template.charAt(0));
|
||||
int currentTokenStartIndex = 0;
|
||||
|
||||
// The parser is implemented as a pointer (marked by `i`) that loops over each character in the template string.
|
||||
// There's majorly two states for the parser, plain-text-mode and mustache-mode, with the current state
|
||||
|
|
@ -118,8 +119,9 @@ public class MustacheHelper {
|
|||
isInsideMustache = true;
|
||||
// Remove the `{` added to the builder.
|
||||
currentToken.deleteCharAt(currentToken.length() - 1);
|
||||
clearAndPushToken(currentToken, tokens);
|
||||
clearAndPushToken(currentToken, currentTokenStartIndex, tokens, false);
|
||||
currentToken.append(prevChar);
|
||||
currentTokenStartIndex = i - 1;
|
||||
braceDepth = 2;
|
||||
}
|
||||
|
||||
|
|
@ -157,13 +159,12 @@ public class MustacheHelper {
|
|||
--braceDepth;
|
||||
currentToken.append(currentChar);
|
||||
if (prevChar == '}' && braceDepth <= 0) {
|
||||
clearAndPushToken(currentToken, tokens);
|
||||
clearAndPushToken(currentToken, currentTokenStartIndex, tokens, true);
|
||||
isInsideMustache = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
currentToken.append(currentChar);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -171,7 +172,7 @@ public class MustacheHelper {
|
|||
}
|
||||
|
||||
if (currentToken.length() > 0) {
|
||||
tokens.add(currentToken.toString());
|
||||
tokens.add(new MustacheBindingToken(currentToken.toString(), currentTokenStartIndex, false));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
|
|
@ -185,15 +186,15 @@ public class MustacheHelper {
|
|||
* @return A Set of strings that serve as replacement keys, with the surrounding double braces stripped and then
|
||||
* trimmed.
|
||||
*/
|
||||
public static Set<String> extractMustacheKeys(String template) {
|
||||
Set<String> keys = new HashSet<>();
|
||||
public static Set<MustacheBindingToken> extractMustacheKeys(String template) {
|
||||
Set<MustacheBindingToken> keys = new HashSet<>();
|
||||
|
||||
for (String token : tokenize(template)) {
|
||||
if (token.startsWith("{{") && token.endsWith("}}")) {
|
||||
for (MustacheBindingToken token : tokenize(template)) {
|
||||
if (token.getValue().startsWith("{{") && token.getValue().endsWith("}}")) {
|
||||
// Allowing empty tokens to be added, to be compatible with the previous `extractMustacheKeys` method.
|
||||
// Calling `.trim()` before adding because Mustache compiler strips keys in the template before looking
|
||||
// up a value. Addresses https://www.notion.so/appsmith/Bindings-with-a-space-at-the-start-fail-to-execute-properly-in-the-API-pane-2eb65d5c6064466b9ef059fa01ef3261
|
||||
keys.add(token.substring(2, token.length() - 2).trim());
|
||||
keys.add(new MustacheBindingToken(token.getValue().substring(2, token.getValue().length() - 2), (token.getStartIndex() + 2), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,23 +202,23 @@ public class MustacheHelper {
|
|||
}
|
||||
|
||||
// For prepared statements we should extract the bindings in order in a list and include duplicate bindings as well.
|
||||
public static List<String> extractMustacheKeysInOrder(String template) {
|
||||
List<String> keys = new ArrayList<>();
|
||||
public static List<MustacheBindingToken> extractMustacheKeysInOrder(String template) {
|
||||
List<MustacheBindingToken> keys = new ArrayList<>();
|
||||
|
||||
for (String token : tokenize(template)) {
|
||||
if (token.startsWith("{{") && token.endsWith("}}")) {
|
||||
for (MustacheBindingToken token : tokenize(template)) {
|
||||
if (token.getValue().startsWith("{{") && token.getValue().endsWith("}}")) {
|
||||
// Allowing empty tokens to be added, to be compatible with the previous `extractMustacheKeys` method.
|
||||
// Calling `.trim()` before adding because Mustache compiler strips keys in the template before looking
|
||||
// up a value. Addresses https://www.notion.so/appsmith/Bindings-with-a-space-at-the-start-fail-to-execute-properly-in-the-API-pane-2eb65d5c6064466b9ef059fa01ef3261
|
||||
keys.add(token.substring(2, token.length() - 2).trim());
|
||||
keys.add(new MustacheBindingToken(token.getValue().substring(2, token.getValue().length() - 2).trim(), (token.getStartIndex() + 2), false));
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static Set<String> extractMustacheKeysFromFields(Object object) {
|
||||
final Set<String> keys = new HashSet<>();
|
||||
public static Set<MustacheBindingToken> extractMustacheKeysFromFields(Object object) {
|
||||
final Set<MustacheBindingToken> keys = new HashSet<>();
|
||||
|
||||
// Linearized recursive search. Instead of calling this function recursively for nested values, we add them to
|
||||
// the end of the queue and process them in a linear fashion. This strategy doesn't suffer from a stack overflow
|
||||
|
|
@ -252,9 +253,9 @@ public class MustacheHelper {
|
|||
return keys;
|
||||
}
|
||||
|
||||
private static void clearAndPushToken(StringBuilder tokenBuilder, List<String> tokenList) {
|
||||
private static void clearAndPushToken(StringBuilder tokenBuilder, int tokenStartIndex, List<MustacheBindingToken> tokenList, boolean includesHandleBars) {
|
||||
if (tokenBuilder.length() > 0) {
|
||||
tokenList.add(tokenBuilder.toString());
|
||||
tokenList.add(new MustacheBindingToken(tokenBuilder.toString(), tokenStartIndex, includesHandleBars));
|
||||
tokenBuilder.setLength(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -322,11 +323,11 @@ public class MustacheHelper {
|
|||
public static String render(String template, Map<String, String> keyValueMap) {
|
||||
final StringBuilder rendered = new StringBuilder();
|
||||
|
||||
for (String token : tokenize(template)) {
|
||||
if (token.startsWith("{{") && token.endsWith("}}")) {
|
||||
rendered.append(keyValueMap.get(token.substring(2, token.length() - 2).trim()));
|
||||
for (MustacheBindingToken token : tokenize(template)) {
|
||||
if (token.getValue().startsWith("{{") && token.getValue().endsWith("}}")) {
|
||||
rendered.append(keyValueMap.get(token.getValue().substring(2, token.getValue().length() - 2).trim()));
|
||||
} else {
|
||||
rendered.append(token);
|
||||
rendered.append(token.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,26 +501,30 @@ public class MustacheHelper {
|
|||
return bindingNames;
|
||||
}
|
||||
|
||||
public static String replaceMustacheWithPlaceholder(String query, List<String> mustacheBindings) {
|
||||
public static String replaceMustacheWithPlaceholder(String query, List<MustacheBindingToken> mustacheBindings) {
|
||||
return replaceMustacheUsingPatterns(query, APPSMITH_SUBSTITUTION_PLACEHOLDER, mustacheBindings, placeholderTrimmingPattern, APPSMITH_SUBSTITUTION_PLACEHOLDER);
|
||||
}
|
||||
|
||||
public static String replaceMustacheWithQuestionMark(String query, List<String> mustacheBindings) {
|
||||
public static String replaceMustacheWithQuestionMark(String query, List<MustacheBindingToken> mustacheBindings) {
|
||||
|
||||
return replaceMustacheUsingPatterns(query, "?", mustacheBindings, quoteQuestionPattern, postQuoteTrimmingQuestionMark);
|
||||
}
|
||||
|
||||
private static String replaceMustacheUsingPatterns(String query,
|
||||
String placeholder,
|
||||
List<String> mustacheBindings,
|
||||
List<MustacheBindingToken> mustacheBindings,
|
||||
Pattern sanitizePattern,
|
||||
String replacement) {
|
||||
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||
actionConfiguration.setBody(query);
|
||||
|
||||
Set<String> mustacheSet = new HashSet<>(mustacheBindings);
|
||||
Set<MustacheBindingToken> mustacheSet = new HashSet<>(mustacheBindings);
|
||||
|
||||
Map<String, String> replaceParamsMap = mustacheSet.stream().collect(Collectors.toMap(Function.identity(), v -> placeholder));
|
||||
Map<String, String> replaceParamsMap = mustacheSet
|
||||
.stream()
|
||||
.map(mustacheToken -> mustacheToken.getValue())
|
||||
.distinct()
|
||||
.collect(Collectors.toMap(k -> k, v -> placeholder));
|
||||
|
||||
// Replace the mustaches with the values mapped to each mustache in replaceParamsMap
|
||||
ActionConfiguration updatedActionConfiguration = renderFieldValues(actionConfiguration, replaceParamsMap);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.appsmith.external.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public class MustacheBindingToken {
|
||||
|
||||
String value;
|
||||
int startIndex;
|
||||
// A token can be with or without handlebars in the value. This boolean value represents the state of the current token.
|
||||
boolean includesHandleBars = false;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.appsmith.external.plugins;
|
|||
import com.appsmith.external.constants.DataType;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -15,6 +16,7 @@ public interface SmartSubstitutionInterface {
|
|||
/**
|
||||
* !Warning! - This function changes the values of arraylist insertedParams which can then be returned
|
||||
* back to the caller with all the values that were finally put during substitution
|
||||
*
|
||||
* @param input
|
||||
* @param mustacheValuesInOrder
|
||||
* @param evaluatedParams
|
||||
|
|
@ -24,7 +26,7 @@ public interface SmartSubstitutionInterface {
|
|||
* @throws AppsmithPluginException
|
||||
*/
|
||||
default Object smartSubstitutionOfBindings(Object input,
|
||||
List<String> mustacheValuesInOrder,
|
||||
List<MustacheBindingToken> mustacheValuesInOrder,
|
||||
List<Param> evaluatedParams,
|
||||
List<Map.Entry<String, String>> insertedParams,
|
||||
Object... args) throws AppsmithPluginException {
|
||||
|
|
@ -32,7 +34,7 @@ public interface SmartSubstitutionInterface {
|
|||
if (mustacheValuesInOrder != null && !mustacheValuesInOrder.isEmpty()) {
|
||||
|
||||
for (int i = 0; i < mustacheValuesInOrder.size(); i++) {
|
||||
String key = mustacheValuesInOrder.get(i);
|
||||
String key = mustacheValuesInOrder.get(i).getValue();
|
||||
Optional<Param> matchingParam = evaluatedParams.stream().filter(param -> param.getKey().trim().equals(key)).findFirst();
|
||||
|
||||
// If the evaluated value of the mustache binding is present, set it in the prepared statement
|
||||
|
|
@ -75,7 +77,7 @@ public interface SmartSubstitutionInterface {
|
|||
|
||||
static <T> T[] append(T[] arr, T lastElement) {
|
||||
final int N = arr.length;
|
||||
arr = Arrays.copyOf(arr, N+1);
|
||||
arr = Arrays.copyOf(arr, N + 1);
|
||||
arr[N] = lastElement;
|
||||
return arr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.models.ActionConfiguration;
|
|||
import com.appsmith.external.models.Connection;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Property;
|
||||
import org.assertj.core.api.AbstractCollectionAssert;
|
||||
import org.assertj.core.api.ObjectAssert;
|
||||
|
|
@ -15,6 +16,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.appsmith.external.helpers.MustacheHelper.extractMustacheKeys;
|
||||
import static com.appsmith.external.helpers.MustacheHelper.extractMustacheKeysFromFields;
|
||||
|
|
@ -29,15 +31,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
)
|
||||
public class MustacheHelperTest {
|
||||
|
||||
private void checkTokens(String template, List<String> expected) {
|
||||
private void checkTokens(String template, List<MustacheBindingToken> expected) {
|
||||
assertThat(tokenize(template)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void checkKeys(String template, Set<String> expected) {
|
||||
private void checkKeys(String template, Set<MustacheBindingToken> expected) {
|
||||
assertThat(extractMustacheKeys(template)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void check(String template, List<String> expectedTokens, Set<String> expectedKeys) {
|
||||
private void check(String template, List<MustacheBindingToken> expectedTokens, Set<MustacheBindingToken> expectedKeys) {
|
||||
if (expectedTokens != null) {
|
||||
checkTokens(template, expectedTokens);
|
||||
}
|
||||
|
|
@ -46,7 +48,7 @@ public class MustacheHelperTest {
|
|||
}
|
||||
}
|
||||
|
||||
private AbstractCollectionAssert<?, Collection<? extends String>, String, ObjectAssert<String>>
|
||||
private AbstractCollectionAssert<?, Collection<? extends MustacheBindingToken>, MustacheBindingToken, ObjectAssert<MustacheBindingToken>>
|
||||
assertKeys(Object object) {
|
||||
return assertThat(extractMustacheKeysFromFields(object));
|
||||
}
|
||||
|
|
@ -58,20 +60,20 @@ public class MustacheHelperTest {
|
|||
|
||||
@Test
|
||||
public void justSingleMustache() {
|
||||
checkTokens("{{A}}", Arrays.asList("{{A}}"));
|
||||
checkKeys("{{A}}", Set.of("A"));
|
||||
checkKeys("{{A + B / C}}", Set.of("A + B / C"));
|
||||
checkTokens("{{A}}", Arrays.asList(new MustacheBindingToken("{{A}}", 0, true)));
|
||||
checkKeys("{{A}}", Set.of(new MustacheBindingToken("A", 2, false)));
|
||||
checkKeys("{{A + B / C}}", Set.of(new MustacheBindingToken("A + B / C", 2, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textAndMustache() {
|
||||
checkKeys("Hello {{name}}", Set.of("name"));
|
||||
checkKeys("Hello {{url.hash}}", Set.of("url.hash"));
|
||||
checkKeys("Hello {{name}}", Set.of(new MustacheBindingToken("name", 8, false)));
|
||||
checkKeys("Hello {{url.hash}}", Set.of(new MustacheBindingToken("url.hash", 8, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustacheAndText() {
|
||||
checkKeys("{{name}} is approved!", Set.of("name"));
|
||||
checkKeys("{{name}} is approved!", Set.of(new MustacheBindingToken("name", 2, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -79,20 +81,20 @@ public class MustacheHelperTest {
|
|||
checkTokens(
|
||||
"Hello {{Customer.Name}}, the status for your order id {{orderId}} is {{status}}",
|
||||
Arrays.asList(
|
||||
"Hello ",
|
||||
"{{Customer.Name}}",
|
||||
", the status for your order id ",
|
||||
"{{orderId}}",
|
||||
" is ",
|
||||
"{{status}}"
|
||||
new MustacheBindingToken("Hello ", 0, false),
|
||||
new MustacheBindingToken("{{Customer.Name}}", 6, true),
|
||||
new MustacheBindingToken(", the status for your order id ", 6, false),
|
||||
new MustacheBindingToken("{{orderId}}", 54, true),
|
||||
new MustacheBindingToken(" is ", 54, false),
|
||||
new MustacheBindingToken("{{status}}", 69, true)
|
||||
)
|
||||
);
|
||||
checkKeys(
|
||||
"Hello {{Customer.Name}}, the status for your order id {{orderId}} is {{status}}",
|
||||
Set.of(
|
||||
"Customer.Name",
|
||||
"orderId",
|
||||
"status"
|
||||
new MustacheBindingToken("Customer.Name", 8, false),
|
||||
new MustacheBindingToken("orderId", 56, false),
|
||||
new MustacheBindingToken("status", 71, false)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -101,11 +103,11 @@ public class MustacheHelperTest {
|
|||
public void realWorldText2() {
|
||||
checkTokens(
|
||||
"{{data.map(datum => {return {id: datum}})}}",
|
||||
Arrays.asList("{{data.map(datum => {return {id: datum}})}}")
|
||||
Arrays.asList(new MustacheBindingToken("{{data.map(datum => {return {id: datum}})}}", 0, true))
|
||||
);
|
||||
checkKeys(
|
||||
"{{data.map(datum => {return {id: datum}})}}",
|
||||
Set.of("data.map(datum => {return {id: datum}})")
|
||||
Set.of(new MustacheBindingToken("data.map(datum => {return {id: datum}})", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -113,35 +115,39 @@ public class MustacheHelperTest {
|
|||
public void braceDances1() {
|
||||
check(
|
||||
"{{}}{{}}}",
|
||||
Arrays.asList("{{}}", "{{}}", "}"),
|
||||
Set.of("")
|
||||
Arrays.asList(
|
||||
new MustacheBindingToken("{{}}", 0, true),
|
||||
new MustacheBindingToken("{{}}", 4, true),
|
||||
new MustacheBindingToken("}", 4, false)),
|
||||
Set.of(new MustacheBindingToken("", 2, false),
|
||||
new MustacheBindingToken("", 6, false))
|
||||
);
|
||||
|
||||
check("{{{}}", Arrays.asList("{{{}}"), Set.of("{"));
|
||||
check("{{{}}", Arrays.asList(new MustacheBindingToken("{{{}}", 0, false)), Set.of(new MustacheBindingToken("{", 2, false)));
|
||||
|
||||
check("{{ {{", Arrays.asList("{{ {{"), Set.of());
|
||||
check("{{ {{", Arrays.asList(new MustacheBindingToken("{{ {{", 0, false)), Set.of());
|
||||
|
||||
check("}} }}", Arrays.asList("}} }}"), Set.of());
|
||||
check("}} }}", Arrays.asList(new MustacheBindingToken("}} }}", 0, false)), Set.of());
|
||||
|
||||
check("}} {{", Arrays.asList("}} ", "{{"), Set.of());
|
||||
check("}} {{", Arrays.asList(new MustacheBindingToken("}} ", 0, false), new MustacheBindingToken("{{", 3, false)), Set.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quotedStrings() {
|
||||
check(
|
||||
"{{ 'abc def'.toUpperCase() }}",
|
||||
Arrays.asList("{{ 'abc def'.toUpperCase() }}"),
|
||||
Set.of("'abc def'.toUpperCase()")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'abc def'.toUpperCase() }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'abc def'.toUpperCase() ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"abc def\".toUpperCase() }}",
|
||||
Arrays.asList("{{ \"abc def\".toUpperCase() }}"),
|
||||
Set.of("\"abc def\".toUpperCase()")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"abc def\".toUpperCase() }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"abc def\".toUpperCase() ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `abc def`.toUpperCase() }}",
|
||||
Arrays.asList("{{ `abc def`.toUpperCase() }}"),
|
||||
Set.of("`abc def`.toUpperCase()")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `abc def`.toUpperCase() }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `abc def`.toUpperCase() ", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -149,38 +155,38 @@ public class MustacheHelperTest {
|
|||
public void singleQuotedStringsWithBraces() {
|
||||
check(
|
||||
"{{ 'The { char is a brace' }}",
|
||||
Arrays.asList("{{ 'The { char is a brace' }}"),
|
||||
Set.of("'The { char is a brace'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'The { char is a brace' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'The { char is a brace' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 'I have {{ two braces' }}",
|
||||
Arrays.asList("{{ 'I have {{ two braces' }}"),
|
||||
Set.of("'I have {{ two braces'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'I have {{ two braces' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'I have {{ two braces' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 'I have {{{ three braces' }}",
|
||||
Arrays.asList("{{ 'I have {{{ three braces' }}"),
|
||||
Set.of("'I have {{{ three braces'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'I have {{{ three braces' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'I have {{{ three braces' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 'The } char is a brace' }}",
|
||||
Arrays.asList("{{ 'The } char is a brace' }}"),
|
||||
Set.of("'The } char is a brace'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'The } char is a brace' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'The } char is a brace' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 'I have }} two braces' }}",
|
||||
Arrays.asList("{{ 'I have }} two braces' }}"),
|
||||
Set.of("'I have }} two braces'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'I have }} two braces' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'I have }} two braces' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 'I have }}} three braces' }}",
|
||||
Arrays.asList("{{ 'I have }}} three braces' }}"),
|
||||
Set.of("'I have }}} three braces'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'I have }}} three braces' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'I have }}} three braces' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 'Interpolation uses {{ and }} delimiters' }}",
|
||||
Arrays.asList("{{ 'Interpolation uses {{ and }} delimiters' }}"),
|
||||
Set.of("'Interpolation uses {{ and }} delimiters'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'Interpolation uses {{ and }} delimiters' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'Interpolation uses {{ and }} delimiters' ", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -188,38 +194,38 @@ public class MustacheHelperTest {
|
|||
public void doubleQuotedStringsWithBraces() {
|
||||
check(
|
||||
"{{ \"The { char is a brace\" }}",
|
||||
Arrays.asList("{{ \"The { char is a brace\" }}"),
|
||||
Set.of("\"The { char is a brace\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"The { char is a brace\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"The { char is a brace\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"I have {{ two braces\" }}",
|
||||
Arrays.asList("{{ \"I have {{ two braces\" }}"),
|
||||
Set.of("\"I have {{ two braces\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"I have {{ two braces\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"I have {{ two braces\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"I have {{{ three braces\" }}",
|
||||
Arrays.asList("{{ \"I have {{{ three braces\" }}"),
|
||||
Set.of("\"I have {{{ three braces\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"I have {{{ three braces\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"I have {{{ three braces\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"The } char is a brace\" }}",
|
||||
Arrays.asList("{{ \"The } char is a brace\" }}"),
|
||||
Set.of("\"The } char is a brace\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"The } char is a brace\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"The } char is a brace\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"I have }} two braces\" }}",
|
||||
Arrays.asList("{{ \"I have }} two braces\" }}"),
|
||||
Set.of("\"I have }} two braces\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"I have }} two braces\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"I have }} two braces\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"I have }}} three braces\" }}",
|
||||
Arrays.asList("{{ \"I have }}} three braces\" }}"),
|
||||
Set.of("\"I have }}} three braces\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"I have }}} three braces\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"I have }}} three braces\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"Interpolation uses {{ and }} delimiters\" }}",
|
||||
Arrays.asList("{{ \"Interpolation uses {{ and }} delimiters\" }}"),
|
||||
Set.of("\"Interpolation uses {{ and }} delimiters\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"Interpolation uses {{ and }} delimiters\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"Interpolation uses {{ and }} delimiters\" ", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -227,38 +233,38 @@ public class MustacheHelperTest {
|
|||
public void backQuotedStringsWithBraces() {
|
||||
check(
|
||||
"{{ `The { char is a brace` }}",
|
||||
Arrays.asList("{{ `The { char is a brace` }}"),
|
||||
Set.of("`The { char is a brace`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `The { char is a brace` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `The { char is a brace` ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `I have {{ two braces` }}",
|
||||
Arrays.asList("{{ `I have {{ two braces` }}"),
|
||||
Set.of("`I have {{ two braces`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `I have {{ two braces` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `I have {{ two braces` ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `I have {{{ three braces` }}",
|
||||
Arrays.asList("{{ `I have {{{ three braces` }}"),
|
||||
Set.of("`I have {{{ three braces`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `I have {{{ three braces` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `I have {{{ three braces` ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `The } char is a brace` }}",
|
||||
Arrays.asList("{{ `The } char is a brace` }}"),
|
||||
Set.of("`The } char is a brace`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `The } char is a brace` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `The } char is a brace` ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `I have }} two braces` }}",
|
||||
Arrays.asList("{{ `I have }} two braces` }}"),
|
||||
Set.of("`I have }} two braces`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `I have }} two braces` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `I have }} two braces` ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `I have }}} three braces` }}",
|
||||
Arrays.asList("{{ `I have }}} three braces` }}"),
|
||||
Set.of("`I have }}} three braces`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `I have }}} three braces` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `I have }}} three braces` ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `Interpolation uses {{ and }} delimiters` }}",
|
||||
Arrays.asList("{{ `Interpolation uses {{ and }} delimiters` }}"),
|
||||
Set.of("`Interpolation uses {{ and }} delimiters`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `Interpolation uses {{ and }} delimiters` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `Interpolation uses {{ and }} delimiters` ", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -266,18 +272,18 @@ public class MustacheHelperTest {
|
|||
public void quotedStringsWithExtras() {
|
||||
check(
|
||||
"{{ 2 + ' hello ' + 3 }}",
|
||||
Arrays.asList("{{ 2 + ' hello ' + 3 }}"),
|
||||
Set.of("2 + ' hello ' + 3")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 2 + ' hello ' + 3 }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 2 + ' hello ' + 3 ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 2 + \" hello \" + 3 }}",
|
||||
Arrays.asList("{{ 2 + \" hello \" + 3 }}"),
|
||||
Set.of("2 + \" hello \" + 3")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 2 + \" hello \" + 3 }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 2 + \" hello \" + 3 ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ 2 + ` hello ` + 3 }}",
|
||||
Arrays.asList("{{ 2 + ` hello ` + 3 }}"),
|
||||
Set.of("2 + ` hello ` + 3")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 2 + ` hello ` + 3 }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 2 + ` hello ` + 3 ", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -285,18 +291,18 @@ public class MustacheHelperTest {
|
|||
public void quotedStringsWithEscapes() {
|
||||
check(
|
||||
"{{ 'Escaped \\' character' }}",
|
||||
Arrays.asList("{{ 'Escaped \\' character' }}"),
|
||||
Set.of("'Escaped \\' character'")
|
||||
Arrays.asList(new MustacheBindingToken("{{ 'Escaped \\' character' }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" 'Escaped \\' character' ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ \"Escaped \\\" character\" }}",
|
||||
Arrays.asList("{{ \"Escaped \\\" character\" }}"),
|
||||
Set.of("\"Escaped \\\" character\"")
|
||||
Arrays.asList(new MustacheBindingToken("{{ \"Escaped \\\" character\" }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" \"Escaped \\\" character\" ", 2, false))
|
||||
);
|
||||
check(
|
||||
"{{ `Escaped \\` character` }}",
|
||||
Arrays.asList("{{ `Escaped \\` character` }}"),
|
||||
Set.of("`Escaped \\` character`")
|
||||
Arrays.asList(new MustacheBindingToken("{{ `Escaped \\` character` }}", 0, true)),
|
||||
Set.of(new MustacheBindingToken(" `Escaped \\` character` ", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -304,8 +310,10 @@ public class MustacheHelperTest {
|
|||
public void conditionalExpression() {
|
||||
check(
|
||||
"Conditional: {{ 2 + 4 ? trueVal : falseVal }}",
|
||||
Arrays.asList("Conditional: ", "{{ 2 + 4 ? trueVal : falseVal }}"),
|
||||
Set.of("2 + 4 ? trueVal : falseVal")
|
||||
Arrays.asList(
|
||||
new MustacheBindingToken("Conditional: ", 0, false),
|
||||
new MustacheBindingToken("{{ 2 + 4 ? trueVal : falseVal }}", 13, true)),
|
||||
Set.of(new MustacheBindingToken(" 2 + 4 ? trueVal : falseVal ", 15, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -313,8 +321,8 @@ public class MustacheHelperTest {
|
|||
public void jsonInMustache() {
|
||||
check(
|
||||
"{{{\"foo\": \"bar\"}}}",
|
||||
Arrays.asList("{{{\"foo\": \"bar\"}}}"),
|
||||
Set.of("{\"foo\": \"bar\"}")
|
||||
Arrays.asList(new MustacheBindingToken("{{{\"foo\": \"bar\"}}}", 0, true)),
|
||||
Set.of(new MustacheBindingToken("{\"foo\": \"bar\"}", 2, false))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -339,11 +347,24 @@ public class MustacheHelperTest {
|
|||
));
|
||||
|
||||
configuration.setProperties(Arrays.asList(
|
||||
new Property("name1", "Hello {{ propertyValue1 }}!"),
|
||||
new Property("name2", "Hello {{ propertyValue2 }}!")
|
||||
new Property("name1", "{{ propertyValue1 }}!"),
|
||||
new Property("name2", "{{ propertyValue2 }}!")
|
||||
));
|
||||
|
||||
Map<String, String> context = Map.of(
|
||||
" dbName ", "rendered dbName",
|
||||
" url ", "rendered url",
|
||||
" headerValue1 ", "rendered headerValue1",
|
||||
" headerValue2 ", "rendered headerValue2",
|
||||
" host1 ", "rendered host1",
|
||||
" host2 ", "rendered host2",
|
||||
" propertyValue1 ", "rendered propertyValue1",
|
||||
" propertyValue2 ", "rendered propertyValue2"
|
||||
);
|
||||
|
||||
assertKeys(configuration).hasSameElementsAs(context.keySet().stream().map(keys -> new MustacheBindingToken(keys, 2, false)).collect(Collectors.toSet()));
|
||||
|
||||
Map<String, String> context2 = Map.of(
|
||||
"dbName", "rendered dbName",
|
||||
"url", "rendered url",
|
||||
"headerValue1", "rendered headerValue1",
|
||||
|
|
@ -354,9 +375,7 @@ public class MustacheHelperTest {
|
|||
"propertyValue2", "rendered propertyValue2"
|
||||
);
|
||||
|
||||
assertKeys(configuration).hasSameElementsAs(context.keySet());
|
||||
|
||||
renderFieldValues(configuration, context);
|
||||
renderFieldValues(configuration, context2);
|
||||
|
||||
assertThat(configuration.getConnection().getDefaultDatabaseName()).isEqualTo("rendered dbName");
|
||||
assertThat(configuration.getUrl()).isEqualTo("rendered url");
|
||||
|
|
@ -372,8 +391,8 @@ public class MustacheHelperTest {
|
|||
);
|
||||
|
||||
assertThat(configuration.getProperties()).containsOnly(
|
||||
new Property("name1", "Hello rendered propertyValue1!"),
|
||||
new Property("name2", "Hello rendered propertyValue2!")
|
||||
new Property("name1", "rendered propertyValue1!"),
|
||||
new Property("name2", "rendered propertyValue2!")
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -408,6 +427,30 @@ public class MustacheHelperTest {
|
|||
));
|
||||
|
||||
final Map<String, String> context = new HashMap<>(Map.of(
|
||||
" body ", "rendered body",
|
||||
" path ", "rendered path",
|
||||
" next ", "rendered next",
|
||||
" headerValue2 ", "rendered headerValue2",
|
||||
" headerValue1 ", "rendered headerValue1",
|
||||
" bodyParam1 ", "rendered bodyParam1",
|
||||
" bodyParam2 ", "rendered bodyParam2",
|
||||
" queryParam1 ", "rendered queryParam1",
|
||||
" queryParam2 ", "rendered queryParam2"
|
||||
));
|
||||
|
||||
context.putAll(Map.of(
|
||||
" pluginSpecifiedProp1 ", "rendered pluginSpecifiedProp1",
|
||||
" pluginSpecifiedProp2 ", "rendered pluginSpecifiedProp2"
|
||||
));
|
||||
|
||||
assertKeys(configuration)
|
||||
.hasSameElementsAs(context
|
||||
.keySet()
|
||||
.stream()
|
||||
.map(keys -> new MustacheBindingToken(keys, 2, false))
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
final Map<String, String> context2 = new HashMap<>(Map.of(
|
||||
"body", "rendered body",
|
||||
"path", "rendered path",
|
||||
"next", "rendered next",
|
||||
|
|
@ -418,15 +461,11 @@ public class MustacheHelperTest {
|
|||
"queryParam1", "rendered queryParam1",
|
||||
"queryParam2", "rendered queryParam2"
|
||||
));
|
||||
|
||||
context.putAll(Map.of(
|
||||
context2.putAll(Map.of(
|
||||
"pluginSpecifiedProp1", "rendered pluginSpecifiedProp1",
|
||||
"pluginSpecifiedProp2", "rendered pluginSpecifiedProp2"
|
||||
));
|
||||
|
||||
assertKeys(configuration).hasSameElementsAs(context.keySet());
|
||||
|
||||
renderFieldValues(configuration, context);
|
||||
renderFieldValues(configuration, context2);
|
||||
|
||||
assertThat(configuration.getBody()).isEqualTo("rendered body");
|
||||
assertThat(configuration.getPath()).isEqualTo("rendered path");
|
||||
|
|
@ -462,7 +501,7 @@ public class MustacheHelperTest {
|
|||
property.setKey("name");
|
||||
property.setValue("Hello {{ \"there\" }}!");
|
||||
configuration.setProperties(Arrays.asList(property));
|
||||
assertKeys(configuration).isEqualTo(Set.of("\"there\""));
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken(" \"there\" ", 8, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -472,7 +511,7 @@ public class MustacheHelperTest {
|
|||
property.setKey("name");
|
||||
property.setValue("Hello {{ \"th\\\\ere\" }}!");
|
||||
configuration.setProperties(Arrays.asList(property));
|
||||
assertKeys(configuration).isEqualTo(Set.of("\"th\\\\ere\""));
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken(" \"th\\\\ere\" ", 8, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -483,14 +522,14 @@ public class MustacheHelperTest {
|
|||
// The `\n` should be interpreted by Javascript, not Java. So we put an extra `\` before it.
|
||||
property.setValue("Hello {{ \"line 1\" + \"\\n\" + \"line 2\" }}!");
|
||||
configuration.setProperties(Arrays.asList(property));
|
||||
assertKeys(configuration).isEqualTo(Set.of("\"line 1\" + \"\\n\" + \"line 2\""));
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken(" \"line 1\" + \"\\n\" + \"line 2\" ", 8, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bodyInMustaches() {
|
||||
ActionConfiguration configuration = new ActionConfiguration();
|
||||
configuration.setBody("outside {{ab}} outside");
|
||||
assertKeys(configuration).isEqualTo(Set.of("ab"));
|
||||
configuration.setBody("outside {{ ab }} outside");
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken(" ab ", 10, false));
|
||||
|
||||
renderFieldValues(configuration, Map.of("ab", "rendered"));
|
||||
assertThat(configuration.getBody()).isEqualTo("outside rendered outside");
|
||||
|
|
@ -500,7 +539,7 @@ public class MustacheHelperTest {
|
|||
public void bodyWithNewlineInMustaches() {
|
||||
ActionConfiguration configuration = new ActionConfiguration();
|
||||
configuration.setBody("outside {{a\nb}} outside");
|
||||
assertKeys(configuration).isEqualTo(Set.of("a\nb"));
|
||||
assertKeys(configuration).isEqualTo(Set.of(new MustacheBindingToken("a\nb", 10, false)));
|
||||
|
||||
renderFieldValues(configuration, Map.of("a\nb", "{\"more\": \"json\"}"));
|
||||
assertThat(configuration.getBody()).isEqualTo("outside {\"more\": \"json\"} outside");
|
||||
|
|
@ -510,21 +549,21 @@ public class MustacheHelperTest {
|
|||
public void bodyWithTabInMustaches() {
|
||||
ActionConfiguration configuration = new ActionConfiguration();
|
||||
configuration.setBody("outside {{a\tb}} outside");
|
||||
assertKeys(configuration).isEqualTo(Set.of("a\tb"));
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken("a\tb", 10, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bodyWithMultilineJavascriptInMustaches() {
|
||||
ActionConfiguration configuration = new ActionConfiguration();
|
||||
configuration.setBody("outside {{\n\ttrue\n\t\t? \"yes\\n\"\n\t\t: \"no\\n\"\n}} outside");
|
||||
assertKeys(configuration).isEqualTo(Set.of("true\n\t\t? \"yes\\n\"\n\t\t: \"no\\n\""));
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken("\n\ttrue\n\t\t? \"yes\\n\"\n\t\t: \"no\\n\"\n", 10, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderBodyWithMultilineJavascriptInMustaches() {
|
||||
ActionConfiguration configuration = new ActionConfiguration();
|
||||
configuration.setBody("outside {{\n\ttrue\n\t\t \"yes\\n\"\n\t\t \"no\\n\"\n}} outside");
|
||||
assertKeys(configuration).isEqualTo(Set.of("true\n\t\t \"yes\\n\"\n\t\t \"no\\n\""));
|
||||
assertKeys(configuration).containsExactlyInAnyOrder(new MustacheBindingToken("\n\ttrue\n\t\t \"yes\\n\"\n\t\t \"no\\n\"\n", 10, false));
|
||||
|
||||
renderFieldValues(configuration, Map.of("true\n\t\t \"yes\\n\"\n\t\t \"no\\n\"", "{\"more\": \"json\"}"));
|
||||
assertThat(configuration.getBody()).isEqualTo("outside {\"more\": \"json\"} outside");
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import com.appsmith.external.models.DBAuth;
|
|||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.external.models.DatasourceTestResult;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.models.RequestParamDTO;
|
||||
|
|
@ -419,7 +420,7 @@ public class AmazonS3Plugin extends BasePlugin {
|
|||
if (TRUE.equals(smartJsonSubstitution)) {
|
||||
final String body = getDataValueSafelyFromFormData(formData, BODY, STRING_TYPE, "");
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(body);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(body);
|
||||
// Replace all the bindings with a placeholder
|
||||
String updatedValue = MustacheHelper.replaceMustacheWithPlaceholder(body, mustacheKeysInOrder);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.appsmith.external.models.ActionExecutionResult;
|
|||
import com.appsmith.external.models.DBAuth;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.PaginationField;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.RequestParamDTO;
|
||||
|
|
@ -147,7 +148,7 @@ public class FirestorePlugin extends BasePlugin {
|
|||
if (query != null) {
|
||||
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedQuery = MustacheHelper.replaceMustacheWithPlaceholder(query, mustacheKeysInOrder);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.appsmith.external.helpers.MustacheHelper;
|
|||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.OAuth2;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
|
|
@ -106,7 +107,7 @@ public class GoogleSheetsPlugin extends BasePlugin {
|
|||
if (property != null) {
|
||||
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(property);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(property);
|
||||
// Replace all the bindings with a placeholder
|
||||
String updatedValue = MustacheHelper.replaceMustacheWithPlaceholder(property, mustacheKeysInOrder);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.appsmith.external.models.ActionExecutionRequest;
|
|||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.ApiContentType;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.PaginationType;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
|
|
@ -89,7 +90,7 @@ public class GraphQLPlugin extends BasePlugin {
|
|||
if (TRUE.equals(smartSubstitution)) {
|
||||
/* Apply smart JSON substitution logic to mustache binding values in query variables */
|
||||
if (!isBlank(variables)) {
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(variables);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(variables);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedVariables = MustacheHelper.replaceMustacheWithPlaceholder(variables, mustacheKeysInOrder);
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ public class GraphQLPlugin extends BasePlugin {
|
|||
/* Apply smart substitution logic to query body */
|
||||
String query = actionConfiguration.getBody();
|
||||
if (!isBlank(query)) {
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedQuery = MustacheHelper.replaceMustacheWithPlaceholder(query, mustacheKeysInOrder);
|
||||
|
||||
|
|
@ -238,8 +239,7 @@ public class GraphQLPlugin extends BasePlugin {
|
|||
return Mono.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (HttpMethod.GET.equals(httpMethod)) {
|
||||
} else if (HttpMethod.GET.equals(httpMethod)) {
|
||||
/**
|
||||
* When a GraphQL request is sent using GET method, the GraphQL body and variables are sent as part of
|
||||
* query parameters in the URL.
|
||||
|
|
@ -247,8 +247,7 @@ public class GraphQLPlugin extends BasePlugin {
|
|||
*/
|
||||
List<Property> additionalQueryParams = getGraphQLQueryParamsForBodyAndVariables(actionConfiguration);
|
||||
uri = uriUtils.addQueryParamsToURI(uri, additionalQueryParams, encodeParamsToggle);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
/**
|
||||
* Only POST and GET HTTP methods are supported by GraphQL specifications.
|
||||
* Ref: https://graphql.org/learn/serving-over-http/
|
||||
|
|
@ -288,8 +287,7 @@ public class GraphQLPlugin extends BasePlugin {
|
|||
if (!isInputQueryBody) {
|
||||
String queryVariables = (String) input;
|
||||
return DataTypeStringUtils.jsonSmartReplacementPlaceholderWithValue(queryVariables, value, null, insertedParams, null, param);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
String queryBody = (String) input;
|
||||
return smartlyReplaceGraphQLQueryBodyPlaceholderWithValue(queryBody, value, insertedParams);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import com.appsmith.external.models.DatasourceConfiguration;
|
|||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.external.models.DatasourceTestResult;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.ParsedDataType;
|
||||
import com.appsmith.external.models.Property;
|
||||
|
|
@ -577,7 +578,7 @@ public class MongoPlugin extends BasePlugin {
|
|||
List<Map.Entry<String, String>> parameters) throws AppsmithPluginException {
|
||||
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(rawQuery);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(rawQuery);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedQuery = MustacheHelper.replaceMustacheWithPlaceholder(rawQuery, mustacheKeysInOrder);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.appsmith.external.models.ActionExecutionResult;
|
|||
import com.appsmith.external.models.DBAuth;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.models.PsParameterDTO;
|
||||
|
|
@ -156,7 +157,7 @@ public class MssqlPlugin extends BasePlugin {
|
|||
|
||||
//Prepared Statement
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedQuery = MustacheHelper.replaceMustacheWithQuestionMark(query, mustacheKeysInOrder);
|
||||
actionConfiguration.setBody(updatedQuery);
|
||||
|
|
@ -166,7 +167,7 @@ public class MssqlPlugin extends BasePlugin {
|
|||
public Mono<ActionExecutionResult> executeCommon(HikariDataSource hikariDSConnection,
|
||||
ActionConfiguration actionConfiguration,
|
||||
Boolean preparedStatement,
|
||||
List<String> mustacheValuesInOrder,
|
||||
List<MustacheBindingToken> mustacheValuesInOrder,
|
||||
ExecuteActionDTO executeActionDTO) {
|
||||
|
||||
final Map<String, Object> requestData = new HashMap<>();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import com.appsmith.external.datatypes.AppsmithType;
|
||||
import com.appsmith.external.datatypes.ClientDataType;
|
||||
import com.appsmith.external.dtos.ExecuteActionDTO;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
|
|
@ -15,6 +14,7 @@ import com.appsmith.external.models.DBAuth;
|
|||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.models.PsParameterDTO;
|
||||
|
|
@ -214,7 +214,7 @@ public class MySqlPlugin extends BasePlugin {
|
|||
|
||||
//This has to be executed as Prepared Statement
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedQuery = MustacheHelper.replaceMustacheWithQuestionMark(query, mustacheKeysInOrder);
|
||||
// Set the query with bindings extracted and replaced with '?' back in config
|
||||
|
|
@ -225,7 +225,7 @@ public class MySqlPlugin extends BasePlugin {
|
|||
public Mono<ActionExecutionResult> executeCommon(Connection connection,
|
||||
ActionConfiguration actionConfiguration,
|
||||
Boolean preparedStatement,
|
||||
List<String> mustacheValuesInOrder,
|
||||
List<MustacheBindingToken> mustacheValuesInOrder,
|
||||
ExecuteActionDTO executeActionDTO,
|
||||
Map<String, Object> requestData) {
|
||||
|
||||
|
|
@ -351,7 +351,7 @@ public class MySqlPlugin extends BasePlugin {
|
|||
private Flux<Result> createAndExecuteQueryFromConnection(String query,
|
||||
Connection connection,
|
||||
Boolean preparedStatement,
|
||||
List<String> mustacheValuesInOrder,
|
||||
List<MustacheBindingToken> mustacheValuesInOrder,
|
||||
ExecuteActionDTO executeActionDTO,
|
||||
Map<String, Object> requestData,
|
||||
Map psParams) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.appsmith.external.models.DBAuth;
|
|||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.models.PsParameterDTO;
|
||||
|
|
@ -236,7 +237,7 @@ public class PostgresPlugin extends BasePlugin {
|
|||
// Prepared Statement
|
||||
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedQuery = MustacheHelper.replaceMustacheWithQuestionMark(query, mustacheKeysInOrder);
|
||||
List<DataType> explicitCastDataTypes = extractExplicitCasting(updatedQuery);
|
||||
|
|
@ -249,7 +250,7 @@ public class PostgresPlugin extends BasePlugin {
|
|||
DatasourceConfiguration datasourceConfiguration,
|
||||
ActionConfiguration actionConfiguration,
|
||||
Boolean preparedStatement,
|
||||
List<String> mustacheValuesInOrder,
|
||||
List<MustacheBindingToken> mustacheValuesInOrder,
|
||||
ExecuteActionDTO executeActionDTO,
|
||||
List<DataType> explicitCastDataTypes) {
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.appsmith.external.models.ActionConfiguration;
|
|||
import com.appsmith.external.models.ActionExecutionRequest;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.PaginationField;
|
||||
import com.appsmith.external.models.PaginationType;
|
||||
import com.appsmith.external.models.Param;
|
||||
|
|
@ -79,7 +80,7 @@ public class RestApiPlugin extends BasePlugin {
|
|||
if (actionConfiguration.getBody() != null) {
|
||||
|
||||
// First extract all the bindings in order
|
||||
List<String> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(actionConfiguration.getBody());
|
||||
List<MustacheBindingToken> mustacheKeysInOrder = MustacheHelper.extractMustacheKeysInOrder(actionConfiguration.getBody());
|
||||
// Replace all the bindings with a ? as expected in a prepared statement.
|
||||
String updatedBody = MustacheHelper.replaceMustacheWithPlaceholder(actionConfiguration.getBody(), mustacheKeysInOrder);
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ public enum AppsmithError {
|
|||
UNKNOWN_PLUGIN_REFERENCE(400, 4052, " Unable to find the plugin. Please reach out to Appsmith customer support to resolve this.", AppsmithErrorAction.DEFAULT, null, ErrorType.CONFIGURATION_ERROR, null),
|
||||
ENV_FILE_NOT_FOUND(500, 5019, "Admin Settings is unavailable. Unable to read and write to Environment file.", AppsmithErrorAction.DEFAULT, null, ErrorType.CONFIGURATION_ERROR, null),
|
||||
PUBLIC_APP_NO_PERMISSION_GROUP(500, 5020, "Invalid state. Public application does not have the required roles set for public access. Please reach out to Appsmith customer support to resolve this.", AppsmithErrorAction.LOG_EXTERNALLY, null, ErrorType.INTERNAL_ERROR, null),
|
||||
RTS_SERVER_ERROR(500, 5021, "RTS server error while processing request: {0}", AppsmithErrorAction.LOG_EXTERNALLY, null, ErrorType.INTERNAL_ERROR, null),
|
||||
;
|
||||
|
||||
private final Integer httpErrorCode;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.appsmith.server.helpers;
|
||||
|
||||
import com.appsmith.external.helpers.MustacheHelper;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
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;
|
||||
|
|
@ -17,11 +17,10 @@ import java.util.regex.Pattern;
|
|||
|
||||
public class DslUtils {
|
||||
|
||||
public static Set<String> getMustacheValueSetFromSpecificDynamicBindingPath(JsonNode dsl, String fieldPath) {
|
||||
public static Set<MustacheBindingToken> getMustacheValueSetFromSpecificDynamicBindingPath(JsonNode dsl, String fieldPath) {
|
||||
|
||||
DslNodeWalkResponse dslWalkResponse = getDslWalkResponse(dsl, fieldPath);
|
||||
|
||||
|
||||
// Only extract mustache keys from leaf nodes
|
||||
if (dslWalkResponse != null && dslWalkResponse.isLeafNode) {
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ public class DslUtils {
|
|||
}
|
||||
|
||||
// Stricter extraction of dynamic bindings
|
||||
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(((TextNode) dslWalkResponse.currentNode).asText());
|
||||
Set<MustacheBindingToken> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(((TextNode) dslWalkResponse.currentNode).asText());
|
||||
return mustacheKeysFromFields;
|
||||
}
|
||||
|
||||
|
|
@ -39,23 +38,29 @@ public class DslUtils {
|
|||
return new HashSet<>();
|
||||
}
|
||||
|
||||
public static JsonNode replaceValuesInSpecificDynamicBindingPath(JsonNode dsl, String fieldPath, Map<String, String> replacementMap) {
|
||||
public static JsonNode replaceValuesInSpecificDynamicBindingPath(JsonNode dsl, String fieldPath, Map<MustacheBindingToken, String> replacementMap) {
|
||||
DslNodeWalkResponse dslWalkResponse = getDslWalkResponse(dsl, fieldPath);
|
||||
|
||||
if (dslWalkResponse != null && dslWalkResponse.isLeafNode) {
|
||||
final String oldValue = ((TextNode) dslWalkResponse.currentNode).asText();
|
||||
final StringBuilder oldValue = new StringBuilder(((TextNode) dslWalkResponse.currentNode).asText());
|
||||
|
||||
final String newValue = StringUtils.replaceEach(
|
||||
oldValue,
|
||||
replacementMap.keySet().toArray(new String[0]),
|
||||
replacementMap.values().toArray(new String[0]));
|
||||
for (MustacheBindingToken mustacheBindingToken : replacementMap.keySet()) {
|
||||
String tokenValue = mustacheBindingToken.getValue();
|
||||
int endIndex = mustacheBindingToken.getStartIndex() + tokenValue.length();
|
||||
if (oldValue.length() >= endIndex && oldValue.subSequence(mustacheBindingToken.getStartIndex(), endIndex).equals(tokenValue)) {
|
||||
oldValue.replace(mustacheBindingToken.getStartIndex(), endIndex, replacementMap.get(mustacheBindingToken));
|
||||
}
|
||||
}
|
||||
|
||||
((ObjectNode) dslWalkResponse.parentNode).set(dslWalkResponse.currentKey, new TextNode(newValue));
|
||||
((ObjectNode) dslWalkResponse.parentNode).set(dslWalkResponse.currentKey, new TextNode(oldValue.toString()));
|
||||
}
|
||||
return dsl;
|
||||
}
|
||||
|
||||
private static DslNodeWalkResponse getDslWalkResponse(JsonNode dsl, String fieldPath) {
|
||||
if (dsl == null) {
|
||||
return null;
|
||||
}
|
||||
String[] fields = fieldPath.split("[].\\[]");
|
||||
// For nested fields, the parent dsl to search in would shift by one level every iteration
|
||||
Object currentNode = dsl;
|
||||
|
|
|
|||
|
|
@ -233,9 +233,9 @@ public class DatabaseChangelog {
|
|||
|
||||
/* Add plugin to the workspace */
|
||||
Update update = new Update();
|
||||
update.addToSet("plugins",new WorkspacePlugin(pluginId, WorkspacePluginStatus.FREE));
|
||||
update.addToSet("plugins", new WorkspacePlugin(pluginId, WorkspacePluginStatus.FREE));
|
||||
|
||||
mongockTemplate.updateMulti(queryToFetchWorkspacesWOPlugin,update,Workspace.class);
|
||||
mongockTemplate.updateMulti(queryToFetchWorkspacesWOPlugin, update, Workspace.class);
|
||||
}
|
||||
|
||||
@ChangeSet(order = "001", id = "initial-plugins", author = "")
|
||||
|
|
@ -755,7 +755,7 @@ public class DatabaseChangelog {
|
|||
}
|
||||
}
|
||||
|
||||
// Examples organization is no longer getting used. Commenting out the migrations which add/update the same.
|
||||
// Examples organization is no longer getting used. Commenting out the migrations which add/update the same.
|
||||
// @SuppressWarnings({"unchecked", "rawtypes"})
|
||||
// @ChangeSet(order = "022", id = "examples-organization", author = "")
|
||||
// public void examplesOrganization(MongockTemplate mongoTemplate, EncryptionService encryptionService) throws IOException {
|
||||
|
|
@ -1886,7 +1886,7 @@ public class DatabaseChangelog {
|
|||
}
|
||||
// Only extract mustache keys from leaf nodes
|
||||
if (parent != null && isLeafNode) {
|
||||
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent);
|
||||
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent).stream().map(token -> token.getValue()).collect(Collectors.toSet());
|
||||
|
||||
// We found the path. But if the path does not have any mustache bindings, remove it from the path list
|
||||
if (mustacheKeysFromFields.isEmpty()) {
|
||||
|
|
@ -3481,9 +3481,9 @@ public class DatabaseChangelog {
|
|||
* This method holds the steps to transform data before it is migrated to UQI schema.
|
||||
* Each transformation is uniquely identified by the combination of plugin name and the transformation name.
|
||||
*
|
||||
* @param pluginName - name of the plugin for which the transformation is intended
|
||||
* @param pluginName - name of the plugin for which the transformation is intended
|
||||
* @param transformationName - name of the transformation relative to the plugin
|
||||
* @param value - value that needs to be transformed
|
||||
* @param value - value that needs to be transformed
|
||||
* @return - transformed value
|
||||
*/
|
||||
public Object transformData(String pluginName, String transformationName, Object value) {
|
||||
|
|
@ -3664,13 +3664,13 @@ public class DatabaseChangelog {
|
|||
* @param dynamicBindingPathList : old dynamicBindingPathList
|
||||
* @param objectMapper
|
||||
* @param action
|
||||
* @param migrationMap : A mapping from `pluginSpecifiedTemplates` index to attribute path in UQI model. For
|
||||
* reference, please check out the `s3MigrationMap` defined above.
|
||||
* @param migrationMap : A mapping from `pluginSpecifiedTemplates` index to attribute path in UQI model. For
|
||||
* reference, please check out the `s3MigrationMap` defined above.
|
||||
* @return : updated dynamicBindingPathList - ported to UQI model.
|
||||
*/
|
||||
static List<Property> getUpdatedDynamicBindingPathList(List<Property> dynamicBindingPathList,
|
||||
ObjectMapper objectMapper, NewAction action,
|
||||
Map<Integer, List<String>> migrationMap) {
|
||||
ObjectMapper objectMapper, NewAction action,
|
||||
Map<Integer, List<String>> migrationMap) {
|
||||
// Return if empty.
|
||||
if (CollectionUtils.isEmpty(dynamicBindingPathList)) {
|
||||
return dynamicBindingPathList;
|
||||
|
|
@ -4101,7 +4101,7 @@ public class DatabaseChangelog {
|
|||
.include(fieldName(QDatasource.datasource.organizationId));
|
||||
|
||||
List<Datasource> datasources = mongockTemplate.find(datasourceQuery, Datasource.class);
|
||||
for(Datasource datasource: datasources) {
|
||||
for (Datasource datasource : datasources) {
|
||||
final Update update = new Update();
|
||||
final String gitSyncId = datasource.getOrganizationId() + "_" + new ObjectId();
|
||||
update.set(fieldName(QDatasource.datasource.gitSyncId), gitSyncId);
|
||||
|
|
@ -4117,7 +4117,7 @@ public class DatabaseChangelog {
|
|||
.addCriteria(where(fieldName(QApplication.application.pages)).exists(true));
|
||||
List<Application> applications = mongockTemplate.find(applicationQuery, Application.class);
|
||||
|
||||
for(Application application: applications) {
|
||||
for (Application application : applications) {
|
||||
application.getPages().forEach(page -> {
|
||||
page.setDefaultPageId(page.getId());
|
||||
});
|
||||
|
|
@ -4179,7 +4179,7 @@ public class DatabaseChangelog {
|
|||
defaultResourceUpdates.set(fieldName(QNewPage.newPage.publishedPage) + "." + "layouts", page.getPublishedPage().getLayouts());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(applicationId) ) {
|
||||
if (!StringUtils.isEmpty(applicationId)) {
|
||||
mongockTemplate.updateFirst(
|
||||
query(where(fieldName(QNewPage.newPage.id)).is(page.getId())),
|
||||
defaultResourceUpdates,
|
||||
|
|
@ -4622,7 +4622,7 @@ public class DatabaseChangelog {
|
|||
);
|
||||
|
||||
// Query to get action id from all Firestore actions
|
||||
Query queryToGetActionIds =query(
|
||||
Query queryToGetActionIds = query(
|
||||
where(fieldName(QNewAction.newAction.pluginId)).is(firestorePlugin.getId())
|
||||
.and(fieldName(QNewAction.newAction.deleted)).ne(true)
|
||||
);
|
||||
|
|
@ -4647,7 +4647,7 @@ public class DatabaseChangelog {
|
|||
ActionDTO unpublishedAction = firestoreAction.getUnpublishedAction();
|
||||
|
||||
// No migrations required if action configuration does not exist.
|
||||
if (unpublishedAction == null || unpublishedAction.getActionConfiguration() == null ) {
|
||||
if (unpublishedAction == null || unpublishedAction.getActionConfiguration() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -4733,6 +4733,7 @@ public class DatabaseChangelog {
|
|||
/**
|
||||
* This method sets the key formData.aggregate.limit to 101 for all Mongo plugin actions.
|
||||
* It iterates over each action id one by one to avoid out of memory error.
|
||||
*
|
||||
* @param mongoActions
|
||||
* @param mongockTemplate
|
||||
*/
|
||||
|
|
@ -4760,6 +4761,7 @@ public class DatabaseChangelog {
|
|||
|
||||
/**
|
||||
* Returns true only if the action has non-null published actionConfiguration.
|
||||
*
|
||||
* @param action
|
||||
* @return true / false
|
||||
*/
|
||||
|
|
@ -4783,6 +4785,7 @@ public class DatabaseChangelog {
|
|||
* Mongo database - this is the same value that would have been applied to the aggregate cmd so far by the
|
||||
* database. However, for any new action, this field's initial value is 10.
|
||||
* Ref: https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/
|
||||
*
|
||||
* @param mongockTemplate
|
||||
*/
|
||||
@ChangeSet(order = "109", id = "add-limit-field-data-to-mongo-aggregate-cmd", author = "")
|
||||
|
|
@ -4804,6 +4807,7 @@ public class DatabaseChangelog {
|
|||
|
||||
/**
|
||||
* Returns true only if the action has non-null un-published actionConfiguration.
|
||||
*
|
||||
* @param action
|
||||
* @return true / false
|
||||
*/
|
||||
|
|
@ -4818,6 +4822,7 @@ public class DatabaseChangelog {
|
|||
|
||||
/**
|
||||
* Fetch an action using id.
|
||||
*
|
||||
* @param actionId
|
||||
* @param mongockTemplate
|
||||
* @return action
|
||||
|
|
@ -4830,6 +4835,7 @@ public class DatabaseChangelog {
|
|||
|
||||
/**
|
||||
* Generate query to fetch all non-deleted actions defined for a given plugin.
|
||||
*
|
||||
* @param plugin
|
||||
* @return query
|
||||
*/
|
||||
|
|
@ -4884,7 +4890,7 @@ public class DatabaseChangelog {
|
|||
query.addCriteria(Criteria.where("deleted").is(FALSE));
|
||||
|
||||
for (Application application : mongockTemplate.find(query, Application.class)) {
|
||||
if(!Optional.ofNullable(application.getGitApplicationMetadata()).isEmpty()) {
|
||||
if (!Optional.ofNullable(application.getGitApplicationMetadata()).isEmpty()) {
|
||||
GitAuth gitAuth = GitDeployKeyGenerator.generateSSHKey(null);
|
||||
GitApplicationMetadata gitApplicationMetadata = application.getGitApplicationMetadata();
|
||||
gitApplicationMetadata.setGitAuth(gitAuth);
|
||||
|
|
@ -4988,6 +4994,7 @@ public class DatabaseChangelog {
|
|||
/**
|
||||
* Adding this migration again because we've added permission to themes.
|
||||
* Also there are couple of changes in the system theme properties.
|
||||
*
|
||||
* @param mongockTemplate
|
||||
* @throws IOException
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -225,7 +225,8 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
|
|||
|
||||
/**
|
||||
* Generates event name tag to analytic events
|
||||
* @param event AnalyticsEvents
|
||||
*
|
||||
* @param event AnalyticsEvents
|
||||
* @param object Analytic event resource object
|
||||
* @return String
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.appsmith.server.services.ce;
|
||||
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
|
@ -23,5 +24,5 @@ public interface AstServiceCE {
|
|||
*/
|
||||
Flux<Tuple2<String, Set<String>>> getPossibleReferencesFromDynamicBinding(List<String> bindingValues, int evalVersion);
|
||||
|
||||
Mono<Map<String, String>> refactorNameInDynamicBindings(Set<String> bindingValues, String oldName, String newName, int evalVersion);
|
||||
Mono<Map<MustacheBindingToken, String>> refactorNameInDynamicBindings(Set<MustacheBindingToken> bindingValues, String oldName, String newName, int evalVersion, boolean isJSObject);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package com.appsmith.server.services.ce;
|
||||
|
||||
import com.appsmith.external.helpers.MustacheHelper;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.server.configurations.CommonConfig;
|
||||
import com.appsmith.server.configurations.InstanceConfig;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.util.WebClientUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
|
@ -10,6 +13,7 @@ import lombok.NoArgsConstructor;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
|
@ -24,6 +28,7 @@ import java.time.Duration;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -76,7 +81,7 @@ public class AstServiceCEImpl implements AstServiceCE {
|
|||
.retrieve()
|
||||
.bodyToMono(GetIdentifiersResponseBulk.class)
|
||||
.retryWhen(Retry.max(3))
|
||||
.flatMapIterable(getIdentifiersResponseDetails -> getIdentifiersResponseDetails.data)
|
||||
.flatMapIterable(getIdentifiersResponse -> getIdentifiersResponse.data)
|
||||
.index()
|
||||
.flatMap(tuple2 -> {
|
||||
long currentIndex = tuple2.getT1();
|
||||
|
|
@ -87,20 +92,27 @@ public class AstServiceCEImpl implements AstServiceCE {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Mono<Map<String, String>> refactorNameInDynamicBindings(Set<String> bindingValues, String oldName, String newName, int evalVersion) {
|
||||
public Mono<Map<MustacheBindingToken, String>> refactorNameInDynamicBindings(Set<MustacheBindingToken> bindingValues, String oldName, String newName, int evalVersion, boolean isJSObject) {
|
||||
if (bindingValues == null || bindingValues.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return Flux.fromIterable(bindingValues)
|
||||
.flatMap(bindingValue -> {
|
||||
EntityRefactorRequest entityRefactorRequest = new EntityRefactorRequest(bindingValue.getValue(), oldName, newName, evalVersion, isJSObject);
|
||||
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)))
|
||||
.body(BodyInserters.fromValue(entityRefactorRequest))
|
||||
.retrieve()
|
||||
.bodyToMono(EntityRefactorResponse.class)
|
||||
.toEntity(EntityRefactorResponse.class)
|
||||
.flatMap(entityRefactorResponseResponseEntity -> {
|
||||
if (HttpStatus.OK.equals(entityRefactorResponseResponseEntity.getStatusCode())) {
|
||||
return Mono.just(Objects.requireNonNull(entityRefactorResponseResponseEntity.getBody()));
|
||||
}
|
||||
return Mono.error(new AppsmithException(AppsmithError.RTS_SERVER_ERROR, entityRefactorResponseResponseEntity.getStatusCodeValue()));
|
||||
})
|
||||
.elapsed()
|
||||
.map(tuple -> {
|
||||
log.debug("Time elapsed since AST refactor call: {} ms", tuple.getT1());
|
||||
|
|
@ -189,6 +201,7 @@ public class AstServiceCEImpl implements AstServiceCE {
|
|||
String oldName;
|
||||
String newName;
|
||||
int evalVersion;
|
||||
Boolean isJSObject;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.appsmith.server.services.ce;
|
|||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.external.models.Datasource;
|
||||
import com.appsmith.external.models.DatasourceTestResult;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.services.CrudService;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
|
@ -21,7 +22,7 @@ public interface DatasourceServiceCE extends CrudService<Datasource, String> {
|
|||
|
||||
Mono<Datasource> findById(String id);
|
||||
|
||||
Set<String> extractKeysFromDatasource(Datasource datasource);
|
||||
Set<MustacheBindingToken> extractKeysFromDatasource(Datasource datasource);
|
||||
|
||||
Mono<Datasource> validateDatasource(Datasource datasource);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.appsmith.external.models.Datasource;
|
|||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceTestResult;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.external.models.QDatasource;
|
||||
import com.appsmith.external.plugins.PluginExecutor;
|
||||
|
|
@ -400,7 +401,7 @@ public class DatasourceServiceCEImpl extends BaseService<DatasourceRepository, D
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> extractKeysFromDatasource(Datasource datasource) {
|
||||
public Set<MustacheBindingToken> extractKeysFromDatasource(Datasource datasource) {
|
||||
if (datasource == null || datasource.getDatasourceConfiguration() == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.appsmith.server.services.ce.ApplicationPageServiceCEImpl.EVALUATION_VERSION;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
|
|
@ -341,7 +342,7 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE {
|
|||
}
|
||||
|
||||
// Stricter extraction of dynamic bindings
|
||||
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent);
|
||||
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent).stream().map(token -> token.getValue()).collect(Collectors.toSet());
|
||||
|
||||
String completePath = widgetName + "." + fieldPath;
|
||||
if (widgetDynamicBindingsMap.containsKey(completePath)) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.appsmith.server.services.ce;
|
||||
|
||||
import com.appsmith.external.helpers.AppsmithBeanUtils;
|
||||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.external.models.DefaultResources;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.ActionCollection;
|
||||
|
|
@ -9,7 +10,6 @@ import com.appsmith.server.domains.NewAction;
|
|||
import com.appsmith.server.domains.NewPage;
|
||||
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||
import com.appsmith.server.dtos.ActionCollectionMoveDTO;
|
||||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.server.dtos.LayoutDTO;
|
||||
import com.appsmith.server.dtos.RefactorActionCollectionNameDTO;
|
||||
import com.appsmith.server.dtos.RefactorActionNameDTO;
|
||||
|
|
@ -38,15 +38,13 @@ import reactor.core.publisher.Mono;
|
|||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject;
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
|
|
@ -319,9 +317,14 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
|
|||
return actionUpdatesFlux
|
||||
.then(actionCollectionService.update(branchedActionCollection.getId(), branchedActionCollection))
|
||||
.then(branchedPageIdMono)
|
||||
.flatMap(branchedPageId -> refactoringSolution.refactorName(branchedPageId, layoutId, oldName, newName));
|
||||
.flatMap(branchedPageId -> refactoringSolution.refactorActionCollectionName(
|
||||
branchedActionCollection.getApplicationId(),
|
||||
branchedPageId,
|
||||
layoutId,
|
||||
oldName,
|
||||
newName));
|
||||
})
|
||||
.map(responseUtils::updateLayoutDTOWithDefaultResources);
|
||||
.map(layoutDTO -> responseUtils.updateLayoutDTOWithDefaultResources(layoutDTO));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.appsmith.server.services.ce;
|
|||
|
||||
import com.appsmith.external.dtos.ExecuteActionDTO;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.NewAction;
|
||||
import com.appsmith.server.domains.NewPage;
|
||||
|
|
@ -38,7 +39,7 @@ public interface NewActionServiceCE extends CrudService<NewAction, String> {
|
|||
Mono<ActionExecutionResult> executeAction(ExecuteActionDTO executeActionDTO);
|
||||
|
||||
Mono<ActionExecutionResult> executeAction(Flux<Part> partsFlux, String branchName);
|
||||
|
||||
|
||||
Mono<ActionDTO> getValidActionForExecution(ExecuteActionDTO executeActionDTO, String actionId, NewAction newAction);
|
||||
|
||||
<T> T variableSubstitution(T configuration, Map<String, String> replaceParamsMap);
|
||||
|
|
@ -85,7 +86,7 @@ public interface NewActionServiceCE extends CrudService<NewAction, String> {
|
|||
|
||||
Mono<List<NewAction>> archiveActionsByApplicationId(String applicationId, AclPermission permission);
|
||||
|
||||
List<String> extractMustacheKeysInOrder(String query);
|
||||
List<MustacheBindingToken> extractMustacheKeysInOrder(String query);
|
||||
|
||||
String replaceMustacheWithQuestionMark(String query, List<String> mustacheBindings);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,16 @@ import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException
|
|||
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
|
||||
import com.appsmith.external.helpers.MustacheHelper;
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.external.models.ActionExecutionRequest;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.ActionProvider;
|
||||
import com.appsmith.external.models.Datasource;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DefaultResources;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.PluginType;
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.models.Provider;
|
||||
|
|
@ -25,7 +29,6 @@ import com.appsmith.server.acl.AclPermission;
|
|||
import com.appsmith.server.acl.PolicyGenerator;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Action;
|
||||
import com.appsmith.external.models.ActionProvider;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.ApplicationMode;
|
||||
import com.appsmith.server.domains.DatasourceContext;
|
||||
|
|
@ -33,9 +36,7 @@ import com.appsmith.server.domains.NewAction;
|
|||
import com.appsmith.server.domains.NewPage;
|
||||
import com.appsmith.server.domains.Page;
|
||||
import com.appsmith.server.domains.Plugin;
|
||||
import com.appsmith.external.models.PluginType;
|
||||
import com.appsmith.server.domains.User;
|
||||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.server.dtos.ActionViewDTO;
|
||||
import com.appsmith.server.dtos.LayoutActionUpdateDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
|
|
@ -106,11 +107,7 @@ import java.util.stream.Collectors;
|
|||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject;
|
||||
import static com.appsmith.external.helpers.DataTypeStringUtils.getDisplayDataTypes;
|
||||
import static com.appsmith.external.helpers.PluginUtils.setValueSafelyInFormData;
|
||||
import static com.appsmith.server.acl.AclPermission.EXECUTE_ACTIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
||||
import static com.appsmith.server.helpers.WidgetSuggestionHelper.getSuggestedWidgets;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
|
@ -447,7 +444,7 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
* @param actionDTO
|
||||
* @return
|
||||
*/
|
||||
private Set<String> extractKeysFromAction(ActionDTO actionDTO) {
|
||||
private Set<MustacheBindingToken> extractKeysFromAction(ActionDTO actionDTO) {
|
||||
if (actionDTO == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
|
@ -457,11 +454,11 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
return new HashSet<>();
|
||||
}
|
||||
|
||||
Set<String> keys = MustacheHelper.extractMustacheKeysFromFields(actionConfiguration);
|
||||
Set<MustacheBindingToken> keys = MustacheHelper.extractMustacheKeysFromFields(actionConfiguration);
|
||||
|
||||
// Add JS function body to jsonPathKeys field.
|
||||
if (PluginType.JS.equals(actionDTO.getPluginType()) && actionConfiguration.getBody() != null) {
|
||||
keys.add(actionConfiguration.getBody());
|
||||
keys.add(new MustacheBindingToken(actionConfiguration.getBody(), 0, false));
|
||||
|
||||
// Since this is a JS function, we should also set the dynamic binding path list if absent
|
||||
List<Property> dynamicBindingPathList = actionDTO.getDynamicBindingPathList();
|
||||
|
|
@ -485,8 +482,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
@Override
|
||||
public NewAction extractAndSetJsonPathKeys(NewAction newAction) {
|
||||
ActionDTO action = newAction.getUnpublishedAction();
|
||||
Set<String> actionKeys = extractKeysFromAction(action);
|
||||
Set<String> datasourceKeys = datasourceService.extractKeysFromDatasource(action.getDatasource());
|
||||
Set<String> actionKeys = extractKeysFromAction(action).stream().map(token -> token.getValue()).collect(Collectors.toSet());
|
||||
Set<String> datasourceKeys = datasourceService.extractKeysFromDatasource(action.getDatasource()).stream().map(token -> token.getValue()).collect(Collectors.toSet());
|
||||
Set<String> keys = new HashSet<>() {{
|
||||
addAll(actionKeys);
|
||||
addAll(datasourceKeys);
|
||||
|
|
@ -1054,6 +1051,7 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
/**
|
||||
* Since we're loading the application and other details from DB *only* for analytics, we check if analytics is
|
||||
* active before making the call to DB.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Boolean isSendExecuteAnalyticsEvent() {
|
||||
|
|
@ -1167,7 +1165,7 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
if (paramsList == null) {
|
||||
paramsList = new ArrayList<>();
|
||||
}
|
||||
List<String> executionParams = paramsList.stream().map(param -> param.getValue()).collect(Collectors.toList());
|
||||
List<String> executionParams = paramsList.stream().map(param -> param.getValue()).collect(Collectors.toList());
|
||||
|
||||
data.putAll(Map.of(
|
||||
"request", request,
|
||||
|
|
@ -1913,7 +1911,7 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
.collectList();
|
||||
}
|
||||
|
||||
public List<String> extractMustacheKeysInOrder(String query) {
|
||||
public List<MustacheBindingToken> extractMustacheKeysInOrder(String query) {
|
||||
return MustacheHelper.extractMustacheKeysInOrder(query);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
package com.appsmith.server.solutions;
|
||||
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.AnalyticsService;
|
||||
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.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.solutions.ce.RefactoringSolutionCEImpl;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
|
@ -26,6 +28,8 @@ public class RefactoringSolutionImpl extends RefactoringSolutionCEImpl implement
|
|||
ApplicationService applicationService,
|
||||
AstService astService,
|
||||
InstanceConfig instanceConfig,
|
||||
AnalyticsService analyticsService,
|
||||
SessionUserService sessionUserService,
|
||||
PagePermission pagePermission,
|
||||
ActionPermission actionPermission) {
|
||||
super(objectMapper,
|
||||
|
|
@ -37,6 +41,8 @@ public class RefactoringSolutionImpl extends RefactoringSolutionCEImpl implement
|
|||
applicationService,
|
||||
astService,
|
||||
instanceConfig,
|
||||
analyticsService,
|
||||
sessionUserService,
|
||||
pagePermission,
|
||||
actionPermission);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ import java.util.stream.Collectors;
|
|||
import static com.appsmith.external.helpers.MustacheHelper.ACTION_ENTITY_REFERENCES;
|
||||
import static com.appsmith.external.helpers.MustacheHelper.WIDGET_ENTITY_REFERENCES;
|
||||
import static com.appsmith.external.helpers.MustacheHelper.getPossibleParents;
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
|
|
@ -934,7 +933,7 @@ public class PageLoadActionsUtilCEImpl implements PageLoadActionsUtilCE {
|
|||
Set<String> mustacheKeysFromFields;
|
||||
// Stricter extraction of dynamic bindings
|
||||
if (isBindingPresentInString) {
|
||||
mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent);
|
||||
mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent).stream().map(token -> token.getValue()).collect(Collectors.toSet());
|
||||
} else {
|
||||
// this must be a JS function. No need to extract mustache. The entire string is JS body
|
||||
mustacheKeysFromFields = Set.of((String) parent);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import com.appsmith.server.dtos.LayoutDTO;
|
|||
import com.appsmith.server.dtos.RefactorActionNameDTO;
|
||||
import com.appsmith.server.dtos.RefactorNameDTO;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface RefactoringSolutionCE {
|
||||
|
||||
|
|
@ -15,5 +18,16 @@ public interface RefactoringSolutionCE {
|
|||
|
||||
Mono<LayoutDTO> refactorActionName(RefactorActionNameDTO refactorActionNameDTO, String branchName);
|
||||
|
||||
Mono<LayoutDTO> refactorName(String pageId, String layoutId, String oldName, String newName);
|
||||
Mono<LayoutDTO> refactorActionCollectionName(String appId, String pageId, String layoutId, String oldName, String newName);
|
||||
|
||||
/**
|
||||
* This method is responsible for the core logic of refactoring a valid name inside an Appsmith page.
|
||||
* This includes refactoring inside the DSL, in actions, and JS objects.
|
||||
* @param pageId The page where the refactor needs to happen
|
||||
* @param layoutId The layout where the refactor needs to happen
|
||||
* @param oldName The valid name to convert from. For JS functions, this would be the FQN
|
||||
* @param newName The new name to convert into. For JS functions, this would be FQN
|
||||
* @return A tuple of the updated layout after refactoring and a set of all the paths in the page that ended up getting refactored
|
||||
*/
|
||||
Mono<Tuple2<LayoutDTO, Set<String>>> refactorName(String pageId, String layoutId, String oldName, String newName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.appsmith.server.solutions.ce;
|
||||
|
||||
import com.appsmith.external.constants.AnalyticsEvents;
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionDTO;
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.appsmith.external.models.PluginType;
|
||||
import com.appsmith.server.configurations.InstanceConfig;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
|
|
@ -17,11 +19,13 @@ 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.AnalyticsService;
|
||||
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.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.solutions.ActionPermission;
|
||||
import com.appsmith.server.solutions.PagePermission;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
|
@ -38,6 +42,7 @@ import reactor.core.publisher.Mono;
|
|||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
|
@ -49,8 +54,6 @@ 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;
|
||||
|
||||
|
|
@ -67,11 +70,8 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
private final PagePermission pagePermission;
|
||||
private final ActionPermission actionPermission;
|
||||
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";
|
||||
|
||||
private final AnalyticsService analyticsService;
|
||||
private final SessionUserService sessionUserService;
|
||||
/*
|
||||
* 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
|
||||
|
|
@ -89,6 +89,8 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
ApplicationService applicationService,
|
||||
AstService astService,
|
||||
InstanceConfig instanceConfig,
|
||||
AnalyticsService analyticsService,
|
||||
SessionUserService sessionUserService,
|
||||
PagePermission pagePermission,
|
||||
ActionPermission actionPermission) {
|
||||
this.objectMapper = objectMapper;
|
||||
|
|
@ -100,26 +102,30 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
this.applicationService = applicationService;
|
||||
this.astService = astService;
|
||||
this.instanceConfig = instanceConfig;
|
||||
this.analyticsService = analyticsService;
|
||||
this.sessionUserService = sessionUserService;
|
||||
this.pagePermission = pagePermission;
|
||||
this.actionPermission = actionPermission;
|
||||
|
||||
// TODO Remove this variable and access the field directly when RTS API is ready
|
||||
this.isRtsAccessible = false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorWidgetName(RefactorNameDTO refactorNameDTO) {
|
||||
final Map<String, String> analyticsProperties = new HashMap<>();
|
||||
String pageId = refactorNameDTO.getPageId();
|
||||
String layoutId = refactorNameDTO.getLayoutId();
|
||||
String oldName = refactorNameDTO.getOldName();
|
||||
String newName = refactorNameDTO.getNewName();
|
||||
return layoutActionService.isNameAllowed(pageId, layoutId, newName)
|
||||
.flatMap(allowed -> {
|
||||
if (!allowed) {
|
||||
.zipWith(newPageService.getById(pageId))
|
||||
.flatMap(tuple -> {
|
||||
analyticsProperties.put(FieldName.APPLICATION_ID, tuple.getT2().getApplicationId());
|
||||
analyticsProperties.put(FieldName.PAGE_ID, pageId);
|
||||
if (!tuple.getT1()) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.NAME_CLASH_NOT_ALLOWED_IN_REFACTOR, oldName, newName));
|
||||
}
|
||||
return this.refactorName(pageId, layoutId, oldName, newName);
|
||||
return this.refactorName(pageId, layoutId, oldName, newName)
|
||||
.flatMap(tuple2 -> this.sendRefactorAnalytics(AnalyticsEvents.REFACTOR_WIDGET.getEventName(), analyticsProperties, tuple2.getT2())
|
||||
.thenReturn(tuple2.getT1()));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -139,6 +145,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorActionName(RefactorActionNameDTO refactorActionNameDTO) {
|
||||
final Map<String, String> analyticsProperties = new HashMap<>();
|
||||
String pageId = refactorActionNameDTO.getPageId();
|
||||
String layoutId = refactorActionNameDTO.getLayoutId();
|
||||
String oldName = refactorActionNameDTO.getOldName();
|
||||
|
|
@ -165,13 +172,23 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
.findActionDTObyIdAndViewMode(actionId, false, actionPermission.getEditPermission());
|
||||
})
|
||||
.flatMap(action -> {
|
||||
analyticsProperties.put(FieldName.APPLICATION_ID, action.getApplicationId());
|
||||
analyticsProperties.put(FieldName.PAGE_ID, pageId);
|
||||
action.setName(newName);
|
||||
if (StringUtils.hasLength(refactorActionNameDTO.getCollectionName())) {
|
||||
action.setFullyQualifiedName(newFullyQualifiedName);
|
||||
}
|
||||
return newActionService.updateUnpublishedAction(actionId, action);
|
||||
})
|
||||
.then(this.refactorName(pageId, layoutId, oldFullyQualifiedName, newFullyQualifiedName));
|
||||
.then(this.refactorName(pageId, layoutId, oldFullyQualifiedName, newFullyQualifiedName)
|
||||
.flatMap(tuple -> {
|
||||
String eventName = AnalyticsEvents.REFACTOR_ACTION.getEventName();
|
||||
if (StringUtils.hasLength(refactorActionNameDTO.getCollectionName())) {
|
||||
eventName = AnalyticsEvents.REFACTOR_JSACTION.getEventName();
|
||||
}
|
||||
return this.sendRefactorAnalytics(eventName, analyticsProperties, tuple.getT2())
|
||||
.thenReturn(tuple.getT1());
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -187,6 +204,16 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
.map(responseUtils::updateLayoutDTOWithDefaultResources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorActionCollectionName(String appId, String pageId, String layoutId, String oldName, String newName) {
|
||||
final Map<String, String> analyticsProperties = new HashMap<>();
|
||||
analyticsProperties.put(FieldName.APPLICATION_ID, appId);
|
||||
analyticsProperties.put(FieldName.PAGE_ID, pageId);
|
||||
return this.refactorName(pageId, layoutId, oldName, newName)
|
||||
.flatMap(tuple -> this.sendRefactorAnalytics(AnalyticsEvents.REFACTOR_JSOBJECT.getEventName(), analyticsProperties, tuple.getT2())
|
||||
.thenReturn(tuple.getT1()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption here is that the refactoring name provided is indeed unique and is fit to be replaced everywhere.
|
||||
* <p>
|
||||
|
|
@ -199,9 +226,10 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
* @return : The DSL after refactor updates
|
||||
*/
|
||||
@Override
|
||||
public Mono<LayoutDTO> refactorName(String pageId, String layoutId, String oldName, String newName) {
|
||||
public Mono<Tuple2<LayoutDTO, Set<String>>> refactorName(String pageId, String layoutId, String oldName, String newName) {
|
||||
String regexPattern = preWord + oldName + postWord;
|
||||
Pattern oldNamePattern = Pattern.compile(regexPattern);
|
||||
final Set<String> updatedBindingPaths = new HashSet<>();
|
||||
|
||||
Mono<PageDTO> pageMono = newPageService
|
||||
// fetch the unpublished page
|
||||
|
|
@ -239,11 +267,12 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
|
||||
final JsonNode dslNode = objectMapper.convertValue(layout.getDsl(), JsonNode.class);
|
||||
Mono<PageDTO> refactorNameInDslMono = this.refactorNameInDsl(dslNode, oldName, newName, evalVersion, oldNamePattern)
|
||||
.then(Mono.fromCallable(() -> {
|
||||
.flatMap(dslBindingPaths -> {
|
||||
updatedBindingPaths.addAll(dslBindingPaths);
|
||||
layout.setDsl(objectMapper.convertValue(dslNode, JSONObject.class));
|
||||
page.setLayouts(layouts);
|
||||
return page;
|
||||
}));
|
||||
return Mono.just(page);
|
||||
});
|
||||
|
||||
// Since the page has most probably changed, save the page and return.
|
||||
return refactorNameInDslMono
|
||||
|
|
@ -284,6 +313,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
if (updates.isEmpty()) {
|
||||
return Mono.just(newAction);
|
||||
}
|
||||
updatedBindingPaths.addAll(updates);
|
||||
if (StringUtils.hasLength(action.getCollectionId())) {
|
||||
updatableCollectionIds.add(action.getCollectionId());
|
||||
}
|
||||
|
|
@ -305,29 +335,23 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
.flatMap(actionCollection -> {
|
||||
final ActionCollectionDTO unpublishedCollection = actionCollection.getUnpublishedCollection();
|
||||
|
||||
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));
|
||||
}
|
||||
return this.replaceValueInMustacheKeys(
|
||||
new HashSet<>(Collections.singletonList(new MustacheBindingToken(unpublishedCollection.getBody(), 0, true))),
|
||||
oldName,
|
||||
newName,
|
||||
evalVersion,
|
||||
oldNamePattern,
|
||||
true)
|
||||
.flatMap(replacedMap -> {
|
||||
Optional<String> replacedValue = replacedMap.values().stream().findFirst();
|
||||
// This value should always be there
|
||||
if (replacedValue.isPresent()) {
|
||||
unpublishedCollection.setBody(replacedValue.get());
|
||||
return actionCollectionService.save(actionCollection);
|
||||
}
|
||||
return Mono.just(actionCollection);
|
||||
});
|
||||
|
||||
})
|
||||
.collectList()
|
||||
.thenReturn(updatedActions);
|
||||
|
|
@ -342,7 +366,8 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
for (Layout layout : layouts) {
|
||||
if (layoutId.equals(layout.getId())) {
|
||||
layout.setDsl(layoutActionService.unescapeMongoSpecialCharacters(layout));
|
||||
return layoutActionService.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout);
|
||||
return layoutActionService.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout)
|
||||
.zipWith(Mono.just(updatedBindingPaths));
|
||||
}
|
||||
}
|
||||
return Mono.empty();
|
||||
|
|
@ -489,7 +514,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
((ObjectNode) bindingPath).set(FieldName.KEY, new TextNode(key));
|
||||
}
|
||||
// Find values inside mustache bindings in this path
|
||||
Set<String> mustacheValues = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(widgetDsl, key);
|
||||
Set<MustacheBindingToken> mustacheValues = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(widgetDsl, key);
|
||||
final String finalKey = key;
|
||||
// Perform refactor for each mustache value
|
||||
return this.replaceValueInMustacheKeys(mustacheValues, oldName, newName, evalVersion, oldNamePattern)
|
||||
|
|
@ -514,7 +539,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
// 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)) {
|
||||
if (Boolean.FALSE.equals(this.instanceConfig.getIsRtsAccessible())) {
|
||||
Set<String> jsonPathKeys = actionDTO.getJsonPathKeys();
|
||||
|
||||
boolean isReferenceFound = false;
|
||||
|
|
@ -546,9 +571,9 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
refactorDynamicBindingsMono = Flux.fromIterable(actionDTO.getDynamicBindingPathList())
|
||||
.flatMap(dynamicBindingPath -> {
|
||||
String key = dynamicBindingPath.getKey();
|
||||
Set<String> mustacheValues = new HashSet<>();
|
||||
Set<MustacheBindingToken> mustacheValues = new HashSet<>();
|
||||
if (PluginType.JS.equals(actionDTO.getPluginType()) && "body".equals(key)) {
|
||||
mustacheValues.add(actionConfiguration.getBody());
|
||||
mustacheValues.add(new MustacheBindingToken(actionConfiguration.getBody(), 0, false));
|
||||
|
||||
} else {
|
||||
mustacheValues = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(actionConfigurationNode, key);
|
||||
|
|
@ -573,19 +598,24 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
return refactorDynamicBindingsMono;
|
||||
}
|
||||
|
||||
Mono<Map<String, String>> replaceValueInMustacheKeys(Set<String> mustacheKeySet, String oldName, String
|
||||
Mono<Map<MustacheBindingToken, String>> replaceValueInMustacheKeys(Set<MustacheBindingToken> 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, oldName, newName, evalVersion, oldNamePattern, false);
|
||||
}
|
||||
|
||||
Mono<Map<MustacheBindingToken, String>> replaceValueInMustacheKeys(Set<MustacheBindingToken> mustacheKeySet, String oldName, String
|
||||
newName, int evalVersion, Pattern oldNamePattern, boolean isJSObject) {
|
||||
if (Boolean.TRUE.equals(this.instanceConfig.getIsRtsAccessible())) {
|
||||
return astService.refactorNameInDynamicBindings(mustacheKeySet, oldName, newName, evalVersion, isJSObject);
|
||||
}
|
||||
return this.replaceValueInMustacheKeys(mustacheKeySet, oldNamePattern, newName);
|
||||
}
|
||||
|
||||
Mono<Map<String, String>> replaceValueInMustacheKeys(Set<String> mustacheKeySet, Pattern
|
||||
Mono<Map<MustacheBindingToken, String>> replaceValueInMustacheKeys(Set<MustacheBindingToken> mustacheKeySet, Pattern
|
||||
oldNamePattern, String newName) {
|
||||
return Flux.fromIterable(mustacheKeySet)
|
||||
.flatMap(mustacheKey -> {
|
||||
Matcher matcher = oldNamePattern.matcher(mustacheKey);
|
||||
Matcher matcher = oldNamePattern.matcher(mustacheKey.getValue());
|
||||
if (matcher.find()) {
|
||||
return Mono.zip(Mono.just(mustacheKey), Mono.just(matcher.replaceAll(newName)));
|
||||
}
|
||||
|
|
@ -593,4 +623,16 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE {
|
|||
})
|
||||
.collectMap(Tuple2::getT1, Tuple2::getT2);
|
||||
}
|
||||
|
||||
Mono<Void> sendRefactorAnalytics(String event, Map<String, String> properties, Set<String> updatedPaths) {
|
||||
return sessionUserService.getCurrentUser()
|
||||
.map(user -> {
|
||||
final Map<String, String> analyticsProperties = new HashMap<>(properties);
|
||||
analyticsProperties.put("updatedPaths", updatedPaths.toString());
|
||||
analyticsProperties.put("userId", user.getUsername());
|
||||
analyticsService.sendEvent(event, user.getUsername(), analyticsProperties);
|
||||
return true;
|
||||
})
|
||||
.then();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
package com.appsmith.server.helpers;
|
||||
|
||||
import com.appsmith.external.models.MustacheBindingToken;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
class DslUtilsTest {
|
||||
|
||||
@Test
|
||||
void getMustacheValueSetFromSpecificDynamicBindingPath_withNullOrEmptyDsl_returnsEmptySet() {
|
||||
Set<MustacheBindingToken> tokensInNullDsl = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(null, "irrelevantPath");
|
||||
Set<MustacheBindingToken> tokensInEmptyDsl = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(new TextNode(""), "irrelevantPath");
|
||||
|
||||
Assertions.assertThat(tokensInNullDsl).isEmpty();
|
||||
Assertions.assertThat(tokensInEmptyDsl).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMustacheValueSetFromSpecificDynamicBindingPath_withComplicatedPathAndMultipleBindings_parsesDslCorrectly() throws JsonProcessingException {
|
||||
String fieldPath = "root.field.list[0].childField.anotherList.0.multidimensionalList[0][0]";
|
||||
String jsonString = "{ " +
|
||||
"\"root\": { " +
|
||||
" \"field\": { " +
|
||||
" \"list\": [ " +
|
||||
" { " +
|
||||
" \"childField\": { " +
|
||||
" \"anotherList\": [ " +
|
||||
" { " +
|
||||
" \"multidimensionalList\" : [ " +
|
||||
" [\"{{ retrievedBinding1.text }} {{ retrievedBinding2.text }}\"]" +
|
||||
" ] " +
|
||||
" } " +
|
||||
" ] " +
|
||||
" } " +
|
||||
" } " +
|
||||
" ] " +
|
||||
" } " +
|
||||
" } " +
|
||||
"}";
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode dsl = mapper.readTree(jsonString);
|
||||
|
||||
Set<MustacheBindingToken> tokens = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(dsl, fieldPath);
|
||||
|
||||
Assertions.assertThat(tokens).containsExactlyInAnyOrder(
|
||||
new MustacheBindingToken(" retrievedBinding1.text ", 2, false),
|
||||
new MustacheBindingToken(" retrievedBinding2.text ", 31, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceValuesInSpecificDynamicBindingPath_whenFieldPathNotFound() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode dsl = mapper.createObjectNode();
|
||||
dsl.put("fieldKey", "fieldValue");
|
||||
JsonNode replacedDsl = DslUtils.replaceValuesInSpecificDynamicBindingPath(dsl, "nonExistentPath", new HashMap<>());
|
||||
Assertions.assertThat(replacedDsl).isEqualTo(dsl);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceValuesInSpecificDynamicBindingPath_whenReplacementKeyNotFound() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode dsl = mapper.createObjectNode();
|
||||
dsl.put("existingPath", "fieldValue");
|
||||
HashMap<MustacheBindingToken, String> replacementMap = new HashMap<>();
|
||||
replacementMap.put(new MustacheBindingToken("nonExistentBinding", 0, false), "newNonExistentBinding");
|
||||
JsonNode replacedDsl = DslUtils.replaceValuesInSpecificDynamicBindingPath(dsl, "existingPath", replacementMap);
|
||||
ObjectNode newDsl = mapper.createObjectNode();
|
||||
newDsl.put("existingPath", "fieldValue");
|
||||
Assertions.assertThat(replacedDsl).isEqualTo(newDsl);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceValuesInSpecificDynamicBindingPath_withSuccessfulMultipleReplacements() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode dsl = mapper.createObjectNode();
|
||||
dsl.put("existingPath", "oldFieldValue1 oldFieldValue2");
|
||||
HashMap<MustacheBindingToken, String> replacementMap = new HashMap<>();
|
||||
replacementMap.put(new MustacheBindingToken("oldFieldValue1", 0, false), "newFieldValue1");
|
||||
replacementMap.put(new MustacheBindingToken("oldFieldValue2", 15, false), "newFieldValue2");
|
||||
JsonNode replacedDsl = DslUtils.replaceValuesInSpecificDynamicBindingPath(dsl, "existingPath", replacementMap);
|
||||
ObjectNode newDsl = mapper.createObjectNode();
|
||||
newDsl.put("existingPath", "newFieldValue1 newFieldValue2");
|
||||
Assertions.assertThat(replacedDsl).isEqualTo(dsl);
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ public class ActionCollectionServiceImplTest {
|
|||
Mockito
|
||||
.when(layoutActionService.updatePageLayoutsByPageId(Mockito.anyString()))
|
||||
.thenAnswer(invocationOnMock -> {
|
||||
return Mono.just(actionCollectionDTO.getPageId());
|
||||
return Mono.just(actionCollectionDTO.getPageId());
|
||||
});
|
||||
|
||||
Mockito
|
||||
|
|
@ -525,7 +525,7 @@ public class ActionCollectionServiceImplTest {
|
|||
Mockito
|
||||
.when(layoutActionService.updatePageLayoutsByPageId(Mockito.anyString()))
|
||||
.thenAnswer(invocationOnMock -> {
|
||||
return Mono.just(actionCollection.getUnpublishedCollection().getPageId());
|
||||
return Mono.just(actionCollection.getUnpublishedCollection().getPageId());
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -799,7 +799,7 @@ public class ActionCollectionServiceImplTest {
|
|||
jsonObject.put("key", "value");
|
||||
layout.setDsl(jsonObject);
|
||||
Mockito
|
||||
.when(refactoringSolution.refactorName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
|
||||
.when(refactoringSolution.refactorActionCollectionName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
|
||||
.thenReturn(Mono.just(layout));
|
||||
|
||||
Mockito
|
||||
|
|
@ -877,7 +877,7 @@ public class ActionCollectionServiceImplTest {
|
|||
layout.setActionUpdates(new ArrayList<>());
|
||||
layout.setLayoutOnLoadActions(new ArrayList<>());
|
||||
Mockito
|
||||
.when(refactoringSolution.refactorName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
|
||||
.when(refactoringSolution.refactorActionCollectionName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
|
||||
.thenReturn(Mono.just(layout));
|
||||
|
||||
Mockito
|
||||
|
|
|
|||
|
|
@ -937,7 +937,7 @@ public class LayoutServiceTest {
|
|||
|
||||
Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("\"anIgnoredAction.data:\" + aGetAction.data"), EVALUATION_VERSION))
|
||||
.thenReturn(Flux.just(Tuples.of("\"anIgnoredAction.data:\" + aGetAction.data", new HashSet<>(Set.of("aGetAction.data")))));
|
||||
String bindingValue = "(function(ignoredAction1){\n" +
|
||||
String bindingValue = "\n(function(ignoredAction1){\n" +
|
||||
"\tlet a = ignoredAction1.data\n" +
|
||||
"\tlet ignoredAction2 = { data: \"nothing\" }\n" +
|
||||
"\tlet b = ignoredAction2.data\n" +
|
||||
|
|
@ -952,8 +952,8 @@ public class LayoutServiceTest {
|
|||
.thenReturn(Flux.just(Tuples.of("aPostActionWithAutoExec.data", new HashSet<>(Set.of("aPostActionWithAutoExec.data")))));
|
||||
Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("aDBAction.data[0].irrelevant"), EVALUATION_VERSION))
|
||||
.thenReturn(Flux.just(Tuples.of("aDBAction.data[0].irrelevant", new HashSet<>(Set.of("aDBAction.data[0].irrelevant")))));
|
||||
Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("anotherDBAction.data.optional"), EVALUATION_VERSION))
|
||||
.thenReturn(Flux.just(Tuples.of("anotherDBAction.data.optional", new HashSet<>(Set.of("anotherDBAction.data.optional")))));
|
||||
Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of(" anotherDBAction.data.optional "), EVALUATION_VERSION))
|
||||
.thenReturn(Flux.just(Tuples.of(" anotherDBAction.data.optional ", new HashSet<>(Set.of("anotherDBAction.data.optional")))));
|
||||
Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("aTableAction.data.child"), EVALUATION_VERSION))
|
||||
.thenReturn(Flux.just(Tuples.of("aTableAction.data.child", new HashSet<>(Set.of("aTableAction.data.child")))));
|
||||
Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("Collection.anAsyncCollectionActionWithoutCall.data"), EVALUATION_VERSION))
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ 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.AnalyticsService;
|
||||
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.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.solutions.ActionPermission;
|
||||
import com.appsmith.server.solutions.ActionPermissionImpl;
|
||||
import com.appsmith.server.solutions.PagePermission;
|
||||
|
|
@ -52,6 +54,10 @@ class RefactoringSolutionCEImplTest {
|
|||
private AstService astService;
|
||||
@MockBean
|
||||
private InstanceConfig instanceConfig;
|
||||
@MockBean
|
||||
private AnalyticsService analyticsService;
|
||||
@MockBean
|
||||
private SessionUserService sessionUserService;
|
||||
|
||||
PagePermission pagePermission;
|
||||
ActionPermission actionPermission;
|
||||
|
|
@ -73,7 +79,9 @@ class RefactoringSolutionCEImplTest {
|
|||
layoutActionService,
|
||||
applicationService,
|
||||
astService,
|
||||
instanceConfig,
|
||||
instanceConfig,
|
||||
analyticsService,
|
||||
sessionUserService,
|
||||
pagePermission,
|
||||
actionPermission);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,13 @@ interface IdentifierNode extends Node {
|
|||
name: string;
|
||||
}
|
||||
|
||||
//Using this to handle the Variable property refactor
|
||||
interface RefactorIdentifierNode extends Node {
|
||||
type: NodeTypes.Identifier;
|
||||
name: string;
|
||||
property?: IdentifierNode;
|
||||
}
|
||||
|
||||
// doc: https://github.com/estree/estree/blob/master/es5.md#variabledeclarator
|
||||
interface VariableDeclaratorNode extends Node {
|
||||
type: NodeTypes.VariableDeclarator;
|
||||
|
|
@ -270,50 +277,81 @@ export const entityRefactorFromCode = (
|
|||
evaluationVersion: number,
|
||||
invalidIdentifiers?: Record<string, unknown>
|
||||
): EntityRefactorResponse => {
|
||||
//Sanitizing leads to removal of special charater.
|
||||
//Hence we are not sanatizing the script. Fix(#18492)
|
||||
//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;
|
||||
//Difference in length of oldName and newName
|
||||
let nameLengthDiff: number = newName.length - oldName.length;
|
||||
const nameLengthDiff: number = newName.length - oldName.length;
|
||||
//Offset index used for deciding location of oldName.
|
||||
let refactorOffset: number = 0;
|
||||
//Count of refactors on the script
|
||||
let refactorCount: number = 0;
|
||||
try {
|
||||
const sanitizedScript = sanitizeScript(script, evaluationVersion);
|
||||
ast = getAST(sanitizedScript);
|
||||
ast = getAST(script);
|
||||
let {
|
||||
references,
|
||||
functionalParams,
|
||||
variableDeclarations,
|
||||
identifierList,
|
||||
}: NodeList = ancestorWalk(ast);
|
||||
let identifierArray = Array.from(identifierList) as Array<IdentifierNode>;
|
||||
Array.from(references).forEach((reference, index) => {
|
||||
const identifierArray = Array.from(
|
||||
identifierList
|
||||
) as Array<RefactorIdentifierNode>;
|
||||
//To handle if oldName has property ("JSObject.myfunc")
|
||||
const oldNameArr = oldName.split(".");
|
||||
const referencesArr = Array.from(references).filter((reference) => {
|
||||
// To remove references derived from declared variables and function params,
|
||||
// We extract the topLevelIdentifier Eg. Api1.name => Api1
|
||||
const topLevelIdentifier = toPath(reference)[0];
|
||||
let shouldUpdateNode = !(
|
||||
return !(
|
||||
functionalParams.has(topLevelIdentifier) ||
|
||||
variableDeclarations.has(topLevelIdentifier) ||
|
||||
has(invalidIdentifiers, topLevelIdentifier)
|
||||
);
|
||||
//check if node should be updated
|
||||
if (shouldUpdateNode && identifierArray[index].name === oldName) {
|
||||
//Replace the oldName by newName
|
||||
//Get start index from node and get subarray from index 0 till start
|
||||
//Append above with new name
|
||||
//Append substring from end index from the node till end of string
|
||||
//Offset variable is used to alter the position based on `refactorOffset`
|
||||
refactorScript =
|
||||
refactorScript.substring(
|
||||
0,
|
||||
identifierArray[index].start + refactorOffset
|
||||
) +
|
||||
newName +
|
||||
refactorScript.substring(identifierArray[index].end + refactorOffset);
|
||||
refactorOffset += nameLengthDiff;
|
||||
++refactorCount;
|
||||
});
|
||||
//Traverse through all identifiers in the script
|
||||
identifierArray.forEach((identifier) => {
|
||||
if (identifier.name === oldNameArr[0]) {
|
||||
let index = 0;
|
||||
while (index < referencesArr.length) {
|
||||
if (identifier.name === referencesArr[index].split(".")[0]) {
|
||||
//Replace the oldName by newName
|
||||
//Get start index from node and get subarray from index 0 till start
|
||||
//Append above with new name
|
||||
//Append substring from end index from the node till end of string
|
||||
//Offset variable is used to alter the position based on `refactorOffset`
|
||||
//In case of nested JS action get end postion fro the property.
|
||||
///Default end index
|
||||
let endIndex = identifier.end;
|
||||
const propertyNode = identifier.property;
|
||||
//Flag variable : true if property should be updated
|
||||
//false if property should not be updated
|
||||
let propertyCondFlag =
|
||||
oldNameArr.length > 1 &&
|
||||
propertyNode &&
|
||||
oldNameArr[1] === propertyNode.name;
|
||||
//Condition to validate if Identifier || Property should be updated??
|
||||
if (oldNameArr.length === 1 || propertyCondFlag) {
|
||||
//Condition to extend end index in case of property match
|
||||
if (propertyCondFlag && propertyNode) {
|
||||
endIndex = propertyNode.end;
|
||||
}
|
||||
refactorScript =
|
||||
refactorScript.substring(0, identifier.start + refactorOffset) +
|
||||
newName +
|
||||
refactorScript.substring(endIndex + refactorOffset);
|
||||
refactorOffset += nameLengthDiff;
|
||||
++refactorCount;
|
||||
//We are only looking for one match in refrence for the identifier name.
|
||||
break;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
});
|
||||
//If script is a JSObject then revert decalartion to export default.
|
||||
|
|
@ -507,8 +545,8 @@ export const extractInvalidTopLevelMemberExpressionsFromCode = (
|
|||
};
|
||||
|
||||
const ancestorWalk = (ast: Node): NodeList => {
|
||||
//List of all Identifier nodes
|
||||
const identifierList = new Array<IdentifierNode>();
|
||||
//List of all Identifier nodes with their property(if exists).
|
||||
const identifierList = new Array<RefactorIdentifierNode>();
|
||||
// List of all references found
|
||||
const references = new Set<string>();
|
||||
// List of variables declared within the script. All identifiers and member expressions derived from declared variables will be removed
|
||||
|
|
@ -554,7 +592,15 @@ const ancestorWalk = (ast: Node): NodeList => {
|
|||
break;
|
||||
}
|
||||
}
|
||||
identifierList.push(node as IdentifierNode);
|
||||
//If parent is a Member expression then attach property to the Node.
|
||||
//else push Identifier Node.
|
||||
const parentNode = ancestors[ancestors.length - 2];
|
||||
if (isMemberExpressionNode(parentNode)) {
|
||||
identifierList.push({
|
||||
...(node as IdentifierNode),
|
||||
property: parentNode.property as IdentifierNode,
|
||||
});
|
||||
} else identifierList.push(node as RefactorIdentifierNode);
|
||||
if (isIdentifierNode(candidateTopLevelNode)) {
|
||||
// If the node is an Identifier, just save that
|
||||
references.add(candidateTopLevelNode.name);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user