fix: fix action objects missing plugin id and plugin type info in database (#13263)
* add plugin id and type info to action object if found missing * this fix is currently added to the read and update action flows
This commit is contained in:
parent
b88cdacc7d
commit
7e9eb58f03
|
|
@ -99,4 +99,5 @@ public interface NewActionServiceCE extends CrudService<NewAction, String> {
|
|||
|
||||
Mono<String> findBranchedIdByBranchNameAndDefaultActionId(String branchName, String defaultActionId, AclPermission permission);
|
||||
|
||||
public Mono<NewAction> sanitizeAction(NewAction action);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
public static final String NATIVE_QUERY_PATH = "formToNativeQuery";
|
||||
public static final String NATIVE_QUERY_PATH_DATA = NATIVE_QUERY_PATH + "." + DATA;
|
||||
public static final String NATIVE_QUERY_PATH_STATUS = NATIVE_QUERY_PATH + "." + STATUS;
|
||||
public static final PluginType JS_PLUGIN_TYPE = PluginType.JS;
|
||||
public static final String JS_PLUGIN_PACKAGE_NAME = "js-plugin";
|
||||
|
||||
private final NewActionRepository repository;
|
||||
private final DatasourceService datasourceService;
|
||||
|
|
@ -371,10 +373,10 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
action.setInvalids(invalids);
|
||||
action.setPluginName(plugin.getName());
|
||||
newAction.setUnpublishedAction(action);
|
||||
newAction.setPluginType(plugin.getType());
|
||||
newAction.setPluginId(plugin.getId());
|
||||
return newAction;
|
||||
}).map(this::extractAndSetJsonPathKeys)
|
||||
})
|
||||
.flatMap(this::sanitizeAction)
|
||||
.map(this::extractAndSetJsonPathKeys)
|
||||
.map(updatedAction -> {
|
||||
// In case of external datasource (not embedded) instead of storing the entire datasource
|
||||
// again inside the action, instead replace it with just the datasource ID. This is so that
|
||||
|
|
@ -1100,7 +1102,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
@Override
|
||||
public Flux<NewAction> findUnpublishedOnLoadActionsExplicitSetByUserInPage(String pageId) {
|
||||
return repository
|
||||
.findUnpublishedActionsByPageIdAndExecuteOnLoadSetByUserTrue(pageId, MANAGE_ACTIONS);
|
||||
.findUnpublishedActionsByPageIdAndExecuteOnLoadSetByUserTrue(pageId, MANAGE_ACTIONS)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1113,27 +1116,32 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
@Override
|
||||
public Flux<NewAction> findUnpublishedActionsInPageByNames(Set<String> names, String pageId) {
|
||||
return repository
|
||||
.findUnpublishedActionsByNameInAndPageId(names, pageId, MANAGE_ACTIONS);
|
||||
.findUnpublishedActionsByNameInAndPageId(names, pageId, MANAGE_ACTIONS)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<NewAction> findById(String id) {
|
||||
return repository.findById(id);
|
||||
return repository.findById(id)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<NewAction> findById(String id, AclPermission aclPermission) {
|
||||
return repository.findById(id, aclPermission);
|
||||
return repository.findById(id, aclPermission)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<NewAction> findByPageId(String pageId, AclPermission permission) {
|
||||
return repository.findByPageId(pageId, permission);
|
||||
return repository.findByPageId(pageId, permission)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<NewAction> findByPageIdAndViewMode(String pageId, Boolean viewMode, AclPermission permission) {
|
||||
return repository.findByPageIdAndViewMode(pageId, viewMode, permission);
|
||||
return repository.findByPageIdAndViewMode(pageId, viewMode, permission)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1151,7 +1159,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
// every created action starts from an unpublishedAction state.
|
||||
|
||||
return Mono.just(action);
|
||||
});
|
||||
})
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1320,9 +1329,11 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
return applicationService
|
||||
.findById(params.getFirst(FieldName.APPLICATION_ID), READ_APPLICATIONS)
|
||||
.flatMapMany(application -> repository.findByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS))
|
||||
.flatMap(this::sanitizeAction)
|
||||
.flatMap(this::setTransientFieldsInUnpublishedAction);
|
||||
}
|
||||
return repository.findAllActionsByNameAndPageIdsAndViewMode(name, pageIds, false, READ_ACTIONS, sort)
|
||||
.flatMap(this::sanitizeAction)
|
||||
.flatMap(this::setTransientFieldsInUnpublishedAction);
|
||||
}
|
||||
|
||||
|
|
@ -1365,6 +1376,75 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
.filter(actionDTO -> !PluginType.JS.equals(actionDTO.getPluginType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is meant to be used to check for any missing or bad values in NewAction object and attempt to fix it.
|
||||
*
|
||||
* This method is added in response to certain cases where it was found that pluginId and pluginType keys
|
||||
* were missing from the NewAction object in the database.Since it is currently not know what exactly causes
|
||||
* these values to go missing, this check will serve as a workaround by fetching and setting pluginId and
|
||||
* pluginType using the datasource object contained in the ActionDTO object.
|
||||
* Ref: https://github.com/appsmithorg/appsmith/issues/11927
|
||||
*
|
||||
*/
|
||||
public Mono<NewAction> sanitizeAction(NewAction action) {
|
||||
Mono<NewAction> actionMono = Mono.just(action);
|
||||
if (isPluginTypeOrPluginIdMissing(action)) {
|
||||
actionMono = providePluginTypeAndIdToNewActionObjectUsingJSTypeOrDatasource(action);
|
||||
}
|
||||
|
||||
return actionMono;
|
||||
}
|
||||
|
||||
private boolean isPluginTypeOrPluginIdMissing(NewAction action) {
|
||||
return action.getPluginId() == null || action.getPluginType() == null;
|
||||
}
|
||||
|
||||
private Mono<NewAction> providePluginTypeAndIdToNewActionObjectUsingJSTypeOrDatasource(NewAction action) {
|
||||
ActionDTO actionDTO = action.getUnpublishedAction();
|
||||
if (actionDTO == null) {
|
||||
return Mono.just(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* if path:
|
||||
* In case an action object is related to a JS Object then it must have a non-null collectionId.
|
||||
*
|
||||
* else path:
|
||||
* Otherwise, check if the datasource object has the pluginId. If so, use this pluginId to fetch the correct
|
||||
* pluginType.
|
||||
*/
|
||||
Datasource datasource = actionDTO.getDatasource();
|
||||
if (actionDTO.getCollectionId() != null) {
|
||||
return setPluginIdAndTypeForJSAction(action);
|
||||
}
|
||||
else if (datasource != null && datasource.getPluginId() != null) {
|
||||
String pluginId = datasource.getPluginId();
|
||||
action.setPluginId(pluginId);
|
||||
|
||||
return setPluginTypeFromId(action, pluginId);
|
||||
}
|
||||
|
||||
return Mono.just(action);
|
||||
}
|
||||
|
||||
private Mono<NewAction> setPluginTypeFromId(NewAction action, String pluginId) {
|
||||
return pluginService.findById(pluginId)
|
||||
.flatMap(plugin -> {
|
||||
action.setPluginType(plugin.getType());
|
||||
return Mono.just(action);
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<NewAction> setPluginIdAndTypeForJSAction(NewAction action) {
|
||||
action.setPluginType(JS_PLUGIN_TYPE);
|
||||
|
||||
return pluginService.findByPackageName(JS_PLUGIN_PACKAGE_NAME)
|
||||
.flatMap(plugin -> {
|
||||
action.setPluginId(plugin.getId());
|
||||
return Mono.just(action);
|
||||
});
|
||||
}
|
||||
|
||||
// We can afford to make this call all the time since we already have all the info we need in context
|
||||
private Mono<DatasourceContext> getRemoteDatasourceContext(Plugin plugin, Datasource datasource) {
|
||||
final DatasourceContext datasourceContext = new DatasourceContext();
|
||||
|
|
@ -1388,7 +1468,9 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
if (action.getGitSyncId() == null) {
|
||||
action.setGitSyncId(action.getApplicationId() + "_" + Instant.now().toString());
|
||||
}
|
||||
return repository.save(action);
|
||||
|
||||
return sanitizeAction(action)
|
||||
.flatMap(sanitizedAction -> repository.save(sanitizedAction));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1396,12 +1478,17 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
actions.stream()
|
||||
.filter(action -> action.getGitSyncId() == null)
|
||||
.forEach(action -> action.setGitSyncId(action.getApplicationId() + "_" + Instant.now().toString()));
|
||||
return repository.saveAll(actions);
|
||||
|
||||
return Flux.fromIterable(actions)
|
||||
.flatMap(this::sanitizeAction)
|
||||
.collectList()
|
||||
.flatMapMany(actionList -> repository.saveAll(actionList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<NewAction> findByPageId(String pageId) {
|
||||
return repository.findByPageId(pageId);
|
||||
return repository.findByPageId(pageId)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1642,7 +1729,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
|||
return repository.findByBranchNameAndDefaultActionId(branchName, defaultActionId, permission)
|
||||
.switchIfEmpty(Mono.error(
|
||||
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ACTION, defaultActionId + "," + branchName))
|
||||
);
|
||||
)
|
||||
.flatMap(this::sanitizeAction);
|
||||
}
|
||||
|
||||
public Mono<String> findBranchedIdByBranchNameAndDefaultActionId(String branchName, String defaultActionId, AclPermission permission) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
package com.appsmith.server.services.ce;
|
||||
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.Datasource;
|
||||
import com.appsmith.server.acl.PolicyGenerator;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.NewAction;
|
||||
import com.appsmith.server.domains.Plugin;
|
||||
import com.appsmith.server.domains.PluginType;
|
||||
import com.appsmith.server.dtos.ActionDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.server.helpers.PluginExecutorHelper;
|
||||
|
|
@ -23,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
|
|
@ -56,6 +62,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@Slf4j
|
||||
|
|
@ -215,4 +227,57 @@ public class NewActionServiceCEImplTest {
|
|||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingPluginIdAndTypeFixForNonJSPluginType() {
|
||||
/* Mock `findById` method of pluginService to return `testPlugin` */
|
||||
Plugin testPlugin = new Plugin();
|
||||
testPlugin.setId("testId");
|
||||
testPlugin.setType(PluginType.DB);
|
||||
Mockito.when(pluginService.findById(anyString()))
|
||||
.thenReturn(Mono.just(testPlugin));
|
||||
|
||||
NewAction action = new NewAction();
|
||||
action.setPluginId(null);
|
||||
action.setPluginType(null);
|
||||
ActionDTO actionDTO = new ActionDTO();
|
||||
Datasource datasource = new Datasource();
|
||||
/* Datasource has correct plugin id */
|
||||
datasource.setPluginId(testPlugin.getId());
|
||||
actionDTO.setDatasource(datasource);
|
||||
action.setUnpublishedAction(actionDTO);
|
||||
|
||||
Mono<NewAction> updatedActionFlux = newActionService.sanitizeAction(action);
|
||||
StepVerifier.create(updatedActionFlux)
|
||||
.assertNext(updatedAction -> {
|
||||
assertEquals("testId", updatedAction.getPluginId());
|
||||
assertEquals(PluginType.DB, updatedAction.getPluginType());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingPluginIdAndTypeFixForJSPluginType() {
|
||||
/* Mock `findByPackageName` method of pluginService to return `testPlugin` */
|
||||
Plugin testPlugin = new Plugin();
|
||||
testPlugin.setId("testId");
|
||||
testPlugin.setType(PluginType.JS);
|
||||
Mockito.when(pluginService.findByPackageName(anyString()))
|
||||
.thenReturn(Mono.just(testPlugin));
|
||||
|
||||
NewAction action = new NewAction();
|
||||
action.setPluginId(null);
|
||||
action.setPluginType(null);
|
||||
ActionDTO actionDTO = new ActionDTO();
|
||||
/* Non-null collection id to indicate a JS action */
|
||||
actionDTO.setCollectionId("testId");
|
||||
action.setUnpublishedAction(actionDTO);
|
||||
|
||||
Mono<NewAction> updatedActionFlux = newActionService.sanitizeAction(action);
|
||||
StepVerifier.create(updatedActionFlux)
|
||||
.assertNext(updatedAction -> {
|
||||
assertEquals("testId", updatedAction.getPluginId());
|
||||
assertEquals(PluginType.JS, updatedAction.getPluginType());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user