chore: refactor crud page flow to move plugin specific handling to plugin module (#26287)

This commit is contained in:
Sumit Kumar 2023-08-18 16:36:19 +05:30 committed by GitHub
parent d26ae16bca
commit a8dcedac7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 18 deletions

View File

@ -21,6 +21,7 @@ import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -313,4 +314,15 @@ public interface PluginExecutor<C> extends ExtensionPoint, CrudTemplateService {
default Mono<DatasourceConfiguration> getDatasourceMetadata(DatasourceConfiguration datasourceConfiguration) {
return Mono.just(datasourceConfiguration);
}
/**
* This method is supposed to provide help with any update required to template queries that are used to create
* the actual select, updated, insert etc. queries as part of the generate CRUD page feature. Any plugin that
* needs special handling should override this method. e.g. in case of the S3 plugin some special handling is
* required because (a) it uses UQI config form (b) it has concept of bucket instead of table.
*/
default Mono<Void> sanitizeGenerateCRUDPageTemplateInfo(
List<ActionConfiguration> actionConfigurationList, Object... args) {
return Mono.empty();
}
}

View File

@ -87,6 +87,8 @@ import static com.external.plugins.constants.FieldName.BUCKET;
import static com.external.plugins.constants.FieldName.COMMAND;
import static com.external.plugins.constants.FieldName.CREATE_DATATYPE;
import static com.external.plugins.constants.FieldName.CREATE_EXPIRY;
import static com.external.plugins.constants.FieldName.KEY_BUCKET;
import static com.external.plugins.constants.FieldName.KEY_DATA;
import static com.external.plugins.constants.FieldName.LIST_EXPIRY;
import static com.external.plugins.constants.FieldName.LIST_PAGINATE;
import static com.external.plugins.constants.FieldName.LIST_PREFIX;
@ -110,6 +112,7 @@ import static com.external.plugins.constants.S3PluginConstants.YES;
import static com.external.utils.DatasourceUtils.getS3ClientBuilder;
import static com.external.utils.TemplateUtils.getTemplates;
import static java.lang.Boolean.TRUE;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
@Slf4j
public class AmazonS3Plugin extends BasePlugin {
@ -1035,5 +1038,28 @@ public class AmazonS3Plugin extends BasePlugin {
.upload(bucketName, path, inputStream, objectMetadata)
.waitForUploadResult();
}
/**
* This method is supposed to provide help with any update required to template queries that are used to create
* the actual select, updated, insert etc. queries as part of the generate CRUD page feature. Any plugin that
* needs special handling should override this method. e.g. in case of the S3 plugin some special handling is
* required because (a) it uses UQI config form (b) it has concept of bucket instead of table.
*/
@Override
public Mono<Void> sanitizeGenerateCRUDPageTemplateInfo(
List<ActionConfiguration> actionConfigurationList, Object... args) {
if (isEmpty(actionConfigurationList)) {
return Mono.empty();
}
/* Add mapping to replace template bucket name with user chosen bucket everywhere in the template */
Map<String, String> mappedColumnsAndTableName = (Map<String, String>) args[0];
final String userSelectedBucketName = (String) args[1];
Map<String, Object> formData = actionConfigurationList.get(0).getFormData();
mappedColumnsAndTableName.put(
(String) ((Map<?, ?>) formData.get(KEY_BUCKET)).get(KEY_DATA), userSelectedBucketName);
return Mono.empty();
}
}
}

View File

@ -36,4 +36,6 @@ public class FieldName {
public static final String LIST_SORT = LIST + "." + SORT;
public static final String LIST_PAGINATE = LIST + "." + PAGINATE;
public static final String SMART_SUBSTITUTION = "smartSubstitution";
public static final String KEY_BUCKET = "bucket";
public static final String KEY_DATA = "data";
}

View File

@ -98,6 +98,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.springframework.util.CollectionUtils.isEmpty;
@Slf4j
public class AmazonS3PluginTest {
@ -1497,4 +1498,32 @@ public class AmazonS3PluginTest {
.assertNext(result -> assertEquals(0, result.getInvalids().size()))
.verifyComplete();
}
@Test
public void verify_sanitizeGenerateCRUDPageTemplateInfo_doesNothing_onEmptyActionConfig() {
AmazonS3Plugin.S3PluginExecutor pluginExecutor = new AmazonS3Plugin.S3PluginExecutor();
List<ActionConfiguration> actionConfigurationList = new ArrayList<>();
Map<String, String> mappedColumnsAndTableName = new HashMap<>();
pluginExecutor
.sanitizeGenerateCRUDPageTemplateInfo(actionConfigurationList, mappedColumnsAndTableName, "test")
.block();
assertEquals(0, actionConfigurationList.size());
assertEquals(true, isEmpty(mappedColumnsAndTableName));
}
@Test
public void verify_sanitizeGenerateCRUDPageTemplateInfo_addsInfoToReplaceTemplateBucket_withUserSelectedBucket() {
Map<String, String> mappedColumnsAndTableName = new HashMap<>();
String userSelectedBucketName = "userBucket";
ActionConfiguration actionConfiguration = new ActionConfiguration();
Map<String, Object> formData = new HashMap<>();
setDataValueSafelyInFormData(formData, "bucket", "templateBucket");
actionConfiguration.setFormData(formData);
AmazonS3Plugin.S3PluginExecutor pluginExecutor = new AmazonS3Plugin.S3PluginExecutor();
pluginExecutor
.sanitizeGenerateCRUDPageTemplateInfo(
List.of(actionConfiguration), mappedColumnsAndTableName, userSelectedBucketName)
.block();
assertEquals(userSelectedBucketName, mappedColumnsAndTableName.get("templateBucket"));
}
}

View File

@ -387,24 +387,26 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
.peek(newAction -> newAction.setDefaultResources(page.getDefaultResources()))
.collect(Collectors.toList());
// Extract S3 bucket name from template application and map to users bucket. Bucket name is stored
// at
// index 1 in plugin specified templates
List<ActionConfiguration> templateUnpublishedActionConfigList = templateActionList.stream()
.map(NewAction::getUnpublishedAction)
.map(ActionDTO::getActionConfiguration)
.collect(Collectors.toList());
if (Entity.S3_PLUGIN_PACKAGE_NAME.equals(plugin.getPackageName())
&& !CollectionUtils.isEmpty(templateActionList)) {
final Map<String, Object> formData = templateActionList
.get(0)
.getUnpublishedAction()
.getActionConfiguration()
.getFormData();
mappedColumnsAndTableName.put(
(String) ((Map<?, ?>) formData.get("bucket")).get("data"), tableName);
}
/**
* Any plugin specific update to the template queries should be defined by overriding the
* `sanitizeGenerateCRUDPageTemplateInfo` method in the respective plugin. In the default case no
* changes are made to the template. e.g. please check the sanitizeGenerateCRUDPageTemplateInfo
* method defined in AmazonS3Plugin.java .
*/
Mono<Void> sanitizeTemplateInfoMono = pluginExecutorHelper
.getPluginExecutorFromPackageName(plugin.getPackageName())
.flatMap(pluginExecutor -> pluginExecutor.sanitizeGenerateCRUDPageTemplateInfo(
templateUnpublishedActionConfigList, mappedColumnsAndTableName, tableName));
log.debug("Going to update layout for page {} and layout {}", savedPageId, layoutId);
return layoutActionService
.updateLayout(savedPageId, page.getApplicationId(), layoutId, layout)
return sanitizeTemplateInfoMono
.then(layoutActionService.updateLayout(
savedPageId, page.getApplicationId(), layoutId, layout))
.then(Mono.zip(
Mono.just(datasourceStorage),
Mono.just(templateActionList),

View File

@ -13,6 +13,7 @@ import com.appsmith.external.models.DatasourceStructure.Table;
import com.appsmith.external.models.DatasourceStructure.TableType;
import com.appsmith.external.models.DefaultResources;
import com.appsmith.external.models.Property;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.GitApplicationMetadata;
@ -60,6 +61,9 @@ import java.util.UUID;
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
@Slf4j
@ExtendWith(SpringExtension.class)
@ -158,16 +162,18 @@ public class CreateDBTablePageSolutionTests {
@MockBean
private PluginExecutorHelper pluginExecutorHelper;
private PluginExecutor spyMockPluginExecutor = spy(new MockPluginExecutor());
private final CRUDPageResourceDTO resource = new CRUDPageResourceDTO();
@BeforeEach
@WithUserDetails(value = "api_user")
public void setup() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any()))
.thenReturn(Mono.just(new MockPluginExecutor()));
Mockito.when(pluginExecutorHelper.getPluginExecutor(any())).thenReturn(Mono.just(spyMockPluginExecutor));
Mockito.when(pluginExecutorHelper.getPluginExecutorFromPackageName(Mockito.anyString()))
.thenReturn(Mono.just(new MockPluginExecutor()));
.thenReturn(Mono.just(spyMockPluginExecutor))
.thenReturn(Mono.just(spyMockPluginExecutor));
if (testWorkspace == null) {
Workspace workspace = new Workspace();
@ -905,6 +911,21 @@ public class CreateDBTablePageSolutionTests {
@Test
@WithUserDetails(value = "api_user")
public void createPageWithNullPageIdForS3() {
/**
* Define handling for sanitizeGenerateCRUDPageTemplateInfo method for S3 plugin on spyMockPluginExecutor.
*/
doAnswer(invocation -> {
List<ActionConfiguration> actionConfigurationList = invocation.getArgument(0);
Map<String, String> mappedColumnsAndTableName = invocation.getArgument(1);
String bucketName = invocation.getArgument(2);
Map<String, Object> formData =
actionConfigurationList.get(0).getFormData();
mappedColumnsAndTableName.put(
(String) ((Map<?, ?>) formData.get("bucket")).get("data"), bucketName);
return Mono.empty();
})
.when(spyMockPluginExecutor)
.sanitizeGenerateCRUDPageTemplateInfo(any(), any(), any());
resource.setApplicationId(testApp.getId());
StringBuilder pluginName = new StringBuilder();