[Bug Fix Improvement] : Table widget keys are unescaped after walking through the DSL (#3908)

* WIP : untested

* Minor refactoring

* Added test case to assert escaping and unescaping of the table widget primary column keys

(cherry picked from commit 174ef284f0)
This commit is contained in:
Trisha Anand 2021-04-07 19:36:37 +05:30 committed by Shrikant Sharat Kandula
parent 2f3c0be5b3
commit 3b83a36bd1
3 changed files with 103 additions and 13 deletions

View File

@ -1,12 +1,10 @@
package com.appsmith.server.helpers;
import com.appsmith.server.constants.FieldName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import java.util.HashMap;
import java.util.Map;
@ -49,18 +47,31 @@ public class WidgetSpecificUtils {
public static JSONObject unEscapeTableWidgetPrimaryColumns(JSONObject dsl) {
String dslAsString;
try {
dslAsString = objectMapper.writeValueAsString(dsl);
dslAsString = dslAsString.replaceAll(FieldName.MONGO_ESCAPE_ID, FieldName.MONGO_UNESCAPED_ID);
dslAsString = dslAsString.replaceAll(FieldName.MONGO_ESCAPE_CLASS, FieldName.MONGO_UNESCAPED_CLASS);
Set<String> keySet = dsl.keySet();
return (JSONObject) jsonParser.parse(dslAsString);
if (keySet.contains(FieldName.PRIMARY_COLUMNS)) {
Map primaryColumns = (Map) dsl.get(FieldName.PRIMARY_COLUMNS);
} catch (JsonProcessingException | ParseException e) {
// Something went wrong in parsing the DSL. Return as is
return dsl;
Map newPrimaryColumns = new HashMap();
Boolean updateRequired = false;
for (String columnName : (Set<String>) primaryColumns.keySet()) {
if (columnName.equals(FieldName.MONGO_ESCAPE_ID)) {
updateRequired = true;
newPrimaryColumns.put(FieldName.MONGO_UNESCAPED_ID, primaryColumns.get(columnName));
} else if (columnName.equals(FieldName.MONGO_ESCAPE_CLASS)) {
updateRequired = true;
newPrimaryColumns.put(FieldName.MONGO_UNESCAPED_CLASS, primaryColumns.get(columnName));
} else {
newPrimaryColumns.put(columnName, primaryColumns.get(columnName));
}
}
if (updateRequired) {
dsl.put(FieldName.PRIMARY_COLUMNS, newPrimaryColumns);
}
}
return dsl;
}
}

View File

@ -694,7 +694,46 @@ public class LayoutActionServiceImpl implements LayoutActionService {
JSONObject dsl = layout.getDsl();
// Unescape specific widgets
dsl = WidgetSpecificUtils.unEscapeTableWidgetPrimaryColumns(dsl);
dsl = unEscapeDslKeys(dsl, layout.getMongoEscapedWidgetNames());
return dsl;
}
private JSONObject unEscapeDslKeys(JSONObject dsl, Set<String> escapedWidgetNames) {
String widgetName = (String) dsl.get(FieldName.WIDGET_NAME);
if (widgetName == null) {
// This isnt a valid widget configuration. No need to traverse further.
return dsl;
}
if (escapedWidgetNames.contains(widgetName)) {
// We should escape the widget keys
String widgetType = dsl.getAsString(FieldName.WIDGET_TYPE);
if (widgetType.equals(FieldName.TABLE_WIDGET)) {
// UnEscape Table widget keys
// Since this is a table widget, it wouldnt have children. We can safely return from here with updated dsl
return WidgetSpecificUtils.unEscapeTableWidgetPrimaryColumns(dsl);
}
}
// Fetch the children of the current node in the DSL and recursively iterate over them to extract bindings
ArrayList<Object> children = (ArrayList<Object>) dsl.get(FieldName.CHILDREN);
ArrayList<Object> newChildren = new ArrayList<>();
if (children != null) {
for (int i = 0; i < children.size(); i++) {
Map data = (Map) children.get(i);
JSONObject object = new JSONObject();
// If the children tag exists and there are entries within it
if (!CollectionUtils.isEmpty(data)) {
object.putAll(data);
JSONObject child = unEscapeDslKeys(object, escapedWidgetNames);
newChildren.add(child);
}
}
dsl.put(FieldName.CHILDREN, newChildren);
}
return dsl;
}

View File

@ -3,6 +3,7 @@ package com.appsmith.server.services;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Layout;
@ -38,6 +39,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -444,4 +446,42 @@ public class LayoutActionServiceTest {
})
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void tableWidgetKeyEscape() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor()));
JSONObject dsl = new JSONObject();
dsl.put("widgetName", "Table1");
dsl.put("type", "TABLE_WIDGET");
Map primaryColumns = new HashMap<String, Object>();
JSONObject jsonObject = new JSONObject(Map.of("key", "value"));
primaryColumns.put("_id", jsonObject);
primaryColumns.put("_class", jsonObject);
dsl.put("primaryColumns", primaryColumns);
Layout layout = testPage.getLayouts().get(0);
layout.setDsl(dsl);
Mono<LayoutDTO> updateLayoutMono = layoutActionService.updateLayout(testPage.getId(), layout.getId(), layout).cache();
Mono<PageDTO> pageFromRepoMono = updateLayoutMono.then(newPageService.findPageById(testPage.getId(), READ_PAGES, false));
StepVerifier
.create(Mono.zip(updateLayoutMono, pageFromRepoMono))
.assertNext(tuple -> {
LayoutDTO updatedLayout = tuple.getT1();
PageDTO pageFromRepo = tuple.getT2();
Map primaryColumns1 = (Map) updatedLayout.getDsl().get("primaryColumns");
assertThat(primaryColumns1.keySet()).containsAll(Set.of(FieldName.MONGO_UNESCAPED_ID, FieldName.MONGO_UNESCAPED_CLASS));
Map primaryColumns2 = (Map) pageFromRepo.getLayouts().get(0).getDsl().get("primaryColumns");
assertThat(primaryColumns2.keySet()).containsAll(Set.of(FieldName.MONGO_ESCAPE_ID, FieldName.MONGO_ESCAPE_CLASS));
})
.verifyComplete();
}
}