Db Migration : Delete unreferenced dynamic binding paths in actions (#3310)
* WIP compute if the dynamic binding path list is correct. * Tested code for deleting the incorrect dynamic binding path list from actions. * Added comments for code readability
This commit is contained in:
parent
9c50182f0e
commit
ca8be4c7f5
|
|
@ -1,5 +1,6 @@
|
||||||
package com.appsmith.server.migrations;
|
package com.appsmith.server.migrations;
|
||||||
|
|
||||||
|
import com.appsmith.external.helpers.MustacheHelper;
|
||||||
import com.appsmith.external.models.BaseDomain;
|
import com.appsmith.external.models.BaseDomain;
|
||||||
import com.appsmith.external.models.DBAuth;
|
import com.appsmith.external.models.DBAuth;
|
||||||
import com.appsmith.external.models.DatasourceConfiguration;
|
import com.appsmith.external.models.DatasourceConfiguration;
|
||||||
|
|
@ -40,6 +41,7 @@ import com.appsmith.server.dtos.OrganizationPluginStatus;
|
||||||
import com.appsmith.server.dtos.PageDTO;
|
import com.appsmith.server.dtos.PageDTO;
|
||||||
import com.appsmith.server.services.EncryptionService;
|
import com.appsmith.server.services.EncryptionService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.github.cloudyrock.mongock.ChangeLog;
|
import com.github.cloudyrock.mongock.ChangeLog;
|
||||||
import com.github.cloudyrock.mongock.ChangeSet;
|
import com.github.cloudyrock.mongock.ChangeSet;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
@ -71,15 +73,18 @@ import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.appsmith.external.helpers.BeanCopyUtils.copyNewFieldValuesIntoOldObject;
|
import static com.appsmith.external.helpers.BeanCopyUtils.copyNewFieldValuesIntoOldObject;
|
||||||
|
|
@ -87,7 +92,10 @@ import static com.appsmith.server.acl.AclPermission.EXECUTE_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MAKE_PUBLIC_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MAKE_PUBLIC_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_INVITE_USERS;
|
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_INVITE_USERS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||||
|
import static com.appsmith.server.helpers.CollectionUtils.isNullOrEmpty;
|
||||||
import static com.appsmith.server.repositories.BaseAppsmithRepositoryImpl.fieldName;
|
import static com.appsmith.server.repositories.BaseAppsmithRepositoryImpl.fieldName;
|
||||||
|
import static java.lang.Boolean.FALSE;
|
||||||
|
import static java.lang.Boolean.TRUE;
|
||||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||||
import static org.springframework.data.mongodb.core.query.Query.query;
|
import static org.springframework.data.mongodb.core.query.Query.query;
|
||||||
import static org.springframework.data.mongodb.core.query.Update.update;
|
import static org.springframework.data.mongodb.core.query.Update.update;
|
||||||
|
|
@ -787,7 +795,7 @@ public class DatabaseChangelog {
|
||||||
final Map<String, Object> datasource = (Map) action.get("datasource");
|
final Map<String, Object> datasource = (Map) action.get("datasource");
|
||||||
datasource.put("pluginId", plugins.get(datasource.remove("$pluginPackageName")));
|
datasource.put("pluginId", plugins.get(datasource.remove("$pluginPackageName")));
|
||||||
datasource.put(FieldName.ORGANIZATION_ID, organizationId);
|
datasource.put(FieldName.ORGANIZATION_ID, organizationId);
|
||||||
if (Boolean.FALSE.equals(datasource.remove("$isEmbedded"))) {
|
if (FALSE.equals(datasource.remove("$isEmbedded"))) {
|
||||||
datasource.put("_id", new ObjectId(datasourceIdsByName.get(datasource.get("name"))));
|
datasource.put("_id", new ObjectId(datasourceIdsByName.get(datasource.get("name"))));
|
||||||
}
|
}
|
||||||
action.put(FieldName.ORGANIZATION_ID, organizationId);
|
action.put(FieldName.ORGANIZATION_ID, organizationId);
|
||||||
|
|
@ -1700,19 +1708,17 @@ public class DatabaseChangelog {
|
||||||
|
|
||||||
for (NewAction action : mongoTemplate.findAll(NewAction.class)) {
|
for (NewAction action : mongoTemplate.findAll(NewAction.class)) {
|
||||||
if (action.getPluginType() != null && action.getPluginType().equals("API")) {
|
if (action.getPluginType() != null && action.getPluginType().equals("API")) {
|
||||||
|
if (action.getUnpublishedAction() != null
|
||||||
|
&& action.getUnpublishedAction().getActionConfiguration() != null) {
|
||||||
|
action.getUnpublishedAction().getActionConfiguration().setEncodeParamsToggle(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.getPublishedAction() != null
|
||||||
|
&& action.getPublishedAction().getActionConfiguration() != null) {
|
||||||
|
action.getPublishedAction().getActionConfiguration().setEncodeParamsToggle(true);
|
||||||
|
}
|
||||||
|
mongoTemplate.save(action);
|
||||||
}
|
}
|
||||||
if (action.getUnpublishedAction() != null
|
|
||||||
&& action.getUnpublishedAction().getActionConfiguration() != null) {
|
|
||||||
action.getUnpublishedAction().getActionConfiguration().setEncodeParamsToggle(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.getPublishedAction() != null
|
|
||||||
&& action.getPublishedAction().getActionConfiguration() != null) {
|
|
||||||
action.getPublishedAction().getActionConfiguration().setEncodeParamsToggle(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
mongoTemplate.save(action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1755,4 +1761,109 @@ public class DatabaseChangelog {
|
||||||
mongoTemplate.save(action);
|
mongoTemplate.save(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ChangeSet(order = "056", id = "fix-dynamicBindingPathListForActions", author = "")
|
||||||
|
public void fixDynamicBindingPathListForExistingActions(MongoTemplate mongoTemplate) {
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
for (NewAction action : mongoTemplate.findAll(NewAction.class)) {
|
||||||
|
|
||||||
|
// We have found an action with dynamic binding path list set by the client.
|
||||||
|
List<Property> dynamicBindingPaths = action.getUnpublishedAction().getDynamicBindingPathList();
|
||||||
|
|
||||||
|
// Only investigate actions which have atleast one dynamic binding path list
|
||||||
|
if (action.getUnpublishedAction().getActionConfiguration() != null && !isNullOrEmpty(dynamicBindingPaths)) {
|
||||||
|
|
||||||
|
List<String> dynamicBindingPathNames = dynamicBindingPaths
|
||||||
|
.stream()
|
||||||
|
.map(property -> property.getKey())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Initialize the final updated binding path list with the existing path names.
|
||||||
|
List<String> finalDynamicBindingPathList = new ArrayList<>();
|
||||||
|
finalDynamicBindingPathList.addAll(dynamicBindingPathNames);
|
||||||
|
|
||||||
|
Set<String> pathsToRemove = new HashSet<>();
|
||||||
|
|
||||||
|
for (String path : dynamicBindingPathNames) {
|
||||||
|
|
||||||
|
if (path != null) {
|
||||||
|
|
||||||
|
String[] fields = path.split("[].\\[]");
|
||||||
|
|
||||||
|
// Convert actionConfiguration into JSON Object and then walk till we reach the path specified.
|
||||||
|
Map<String, Object> actionConfigurationMap = mapper.convertValue(action.getUnpublishedAction().getActionConfiguration(), Map.class);
|
||||||
|
Object parent = new JSONObject(actionConfigurationMap);
|
||||||
|
Iterator<String> fieldsIterator = Arrays.stream(fields).filter(fieldToken -> !fieldToken.isBlank()).iterator();
|
||||||
|
Boolean isLeafNode = false;
|
||||||
|
|
||||||
|
while (fieldsIterator.hasNext()) {
|
||||||
|
String nextKey = fieldsIterator.next();
|
||||||
|
if (parent instanceof JSONObject) {
|
||||||
|
parent = ((JSONObject) parent).get(nextKey);
|
||||||
|
} else if (parent instanceof Map) {
|
||||||
|
parent = ((Map<String, ?>) parent).get(nextKey);
|
||||||
|
} else if (parent instanceof List) {
|
||||||
|
if (Pattern.matches(Pattern.compile("[0-9]+").toString(), nextKey)) {
|
||||||
|
try {
|
||||||
|
parent = ((List) parent).get(Integer.parseInt(nextKey));
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
// The index being referred does not exist. Hence the path would not exist.
|
||||||
|
pathsToRemove.add(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Parent is a list but does not match the pattern. Hence the path would not exist.
|
||||||
|
pathsToRemove.add(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After updating the parent, check for the types
|
||||||
|
if (parent == null) {
|
||||||
|
pathsToRemove.add(path);
|
||||||
|
break;
|
||||||
|
} else if (parent instanceof String) {
|
||||||
|
// If we get String value, then this is a leaf node
|
||||||
|
isLeafNode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only extract mustache keys from leaf nodes
|
||||||
|
if (parent != null && isLeafNode) {
|
||||||
|
Set<String> mustacheKeysFromFields = MustacheHelper.extractMustacheKeysFromFields(parent);
|
||||||
|
|
||||||
|
// We found the path. But if the path does not have any mustache bindings, remove it from the path list
|
||||||
|
if (mustacheKeysFromFields.isEmpty()) {
|
||||||
|
pathsToRemove.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean actionEdited = pathsToRemove.size() > 0 ? TRUE : FALSE;
|
||||||
|
|
||||||
|
// Only update the action if required
|
||||||
|
if (actionEdited) {
|
||||||
|
// We have walked all the dynamic binding paths which either dont exist or they exist but don't contain any mustache bindings
|
||||||
|
for (String path : dynamicBindingPathNames) {
|
||||||
|
if (pathsToRemove.contains(path)) {
|
||||||
|
finalDynamicBindingPathList.remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Property> updatedDynamicBindingPathList = finalDynamicBindingPathList
|
||||||
|
.stream()
|
||||||
|
.map(path -> {
|
||||||
|
Property property = new Property();
|
||||||
|
property.setKey(path);
|
||||||
|
return property;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
action.getUnpublishedAction().setDynamicBindingPathList(updatedDynamicBindingPathList);
|
||||||
|
mongoTemplate.save(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user