diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/FieldName.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/FieldName.java index 7489f05e4c..2a8dec7585 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/FieldName.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/FieldName.java @@ -162,5 +162,6 @@ public class FieldName { public static final String UNASSIGNED_USER_GROUPS_FROM_PERMISSION_GROUPS = "unAssignedGroups"; public static final String ASSIGNED_TO_PERMISSION_GROUPS = "assignedUserAndGroups"; public static final String UNASSIGNED_FROM_PERMISSION_GROUPS = "unAssignedUsersAndGroups"; + public static final String ENVIRONMENT_NAME ="environmentName"; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ActionControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ActionControllerCE.java index 1725b61880..800d2bcb70 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ActionControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ActionControllerCE.java @@ -74,8 +74,9 @@ public class ActionControllerCE { @PostMapping(value = "/execute", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Mono> executeAction(@RequestBody Flux partFlux, - @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { - return newActionService.executeAction(partFlux, branchName) + @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName, + @RequestHeader(name = FieldName.ENVIRONMENT_NAME, required = false) String environmentName) { + return newActionService.executeAction(partFlux, branchName, environmentName) .map(updatedResource -> new ResponseDTO<>(HttpStatus.OK.value(), updatedResource, null)); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java index f90473ed01..981e6a00e9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java @@ -36,9 +36,9 @@ public interface NewActionServiceCE extends CrudService { Mono updateUnpublishedAction(String id, ActionDTO action); - Mono executeAction(ExecuteActionDTO executeActionDTO); + Mono executeAction(ExecuteActionDTO executeActionDTO, String environmentName); - Mono executeAction(Flux partsFlux, String branchName); + Mono executeAction(Flux partsFlux, String branchName, String environmentName); Mono getValidActionForExecution(ExecuteActionDTO executeActionDTO, String actionId, NewAction newAction); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java index e0a8a1fa1e..f4f020dca3 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java @@ -24,6 +24,9 @@ import com.appsmith.external.models.Policy; import com.appsmith.external.models.Property; import com.appsmith.external.models.Provider; import com.appsmith.external.models.RequestParamDTO; +import com.appsmith.external.models.ActionProvider; +import com.appsmith.external.models.PluginType; +import com.appsmith.external.models.ActionDTO; import com.appsmith.external.plugins.PluginExecutor; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.acl.PolicyGenerator; @@ -83,6 +86,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.util.function.Tuple2; +import reactor.util.function.Tuple5; import javax.lang.model.SourceVersion; import javax.validation.Validator; @@ -629,10 +633,12 @@ public class NewActionServiceCEImpl extends BaseService executeAction(ExecuteActionDTO executeActionDTO) { - // 1. Validate input parameters which are required for mustache replacements - List params = executeActionDTO.getParams(); + /** + * Sets the param value to "" if key is not empty and value is null for each param + * @param params + */ + protected void replaceNullWithQuotesForParamValues(List params) { + if (!CollectionUtils.isEmpty(params)) { for (Param param : params) { // In case the parameter values turn out to be null, set it to empty string instead to allow @@ -642,157 +648,285 @@ public class NewActionServiceCEImpl extends BaseService actionName = new AtomicReference<>(); - // Initialize the name to be empty value - actionName.set(""); - // 2. Fetch the action from the DB and check if it can be executed - Mono actionMono = repository.findById(actionId, actionPermission.getExecutePermission()) + /** + * Fetches and caches action with permission. + * @param actionId + * @return actionMono + */ + protected Mono getCachedActionForActionExecution(String actionId) { + + return repository.findById(actionId, actionPermission.getExecutePermission()) .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ACTION, actionId))) .cache(); + } - Mono actionDTOMono = actionMono + /** + * Retrieves and caches validated actionDTO from actionMono. + * @param actionMono + * @param executeActionDTO + * @param actionId + * @return actionDTOMono + */ + protected Mono getCachedActionDTOForActionExecution(Mono actionMono, + ExecuteActionDTO executeActionDTO, + String actionId) { + return actionMono .flatMap(action -> getValidActionForExecution(executeActionDTO, actionId, action)) .cache(); + } - // 3. Instantiate the implementation class based on the query type + /** + * Fetches, validates and caches the datasource from actionDTO + * @param actionDTOMono + * @return datasourceMono + */ + protected Mono getCachedDatasourceForActionExecution(Mono actionDTOMono) { - Mono datasourceMono = actionDTOMono - .flatMap(actionDTO -> datasourceService.getValidDatasourceFromActionMono(actionDTO, datasourcePermission.getExecutePermission())) - .cache(); - - Mono pluginMono = datasourceMono + return actionDTOMono + .flatMap(actionDTO -> datasourceService.getValidDatasourceFromActionMono(actionDTO, + datasourcePermission.getExecutePermission())) .flatMap(datasource -> { - // For embedded datasources, validate the datasource for each execution + // For embedded datasource, validate the datasource for each execution if (datasource.getId() == null) { return datasourceService.validateDatasource(datasource); } - // The external datasources have already been validated. No need to validate again. + // The external datasource have already been validated. No need to validate again. return Mono.just(datasource); }) + .cache(); + } + + /** + * fetches and caches plugin by pluginId after checking datasource for invalids(issues) + * @param datasourceMono + * @param actionId + * @return pluginMono if datasource has no issues and plugin is find, else throws error + */ + protected Mono getCachedPluginForActionExecution(Mono datasourceMono, String actionId) { + return datasourceMono .flatMap(datasource -> { Set invalids = datasource.getInvalids(); if (!CollectionUtils.isEmpty(invalids)) { log.error("Unable to execute actionId: {} because it's datasource is not valid. Cause: {}", - actionId, ArrayUtils.toString(invalids)); + actionId, ArrayUtils.toString(invalids)); return Mono.error(new AppsmithException(AppsmithError.INVALID_DATASOURCE, - datasource.getName(), - ArrayUtils.toString(invalids))); + datasource.getName(), + ArrayUtils.toString(invalids))); } return pluginService.findById(datasource.getPluginId()); }) .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.PLUGIN))) .cache(); + } - Mono pluginExecutorMono = pluginExecutorHelper.getPluginExecutor(pluginMono); + /** + * Fetches and returns editorConfigLabelMap if datasourceId is present + * @param datasourceMono + * @return an Empty hashMap if datasource doesn't have id, else configLabelMap from plugin service + */ + protected Mono getEditorConfigLabelMap (Mono datasourceMono) { - // 4. Execute the query - Mono actionExecutionResultMono = Mono - .zip( - actionDTOMono, - datasourceMono, - pluginExecutorMono, - pluginMono - ) + return datasourceMono + .flatMap(datasource -> { + if (!StringUtils.hasLength(datasource.getId())) { + return Mono.just(new HashMap()); + } + + return pluginService.getEditorConfigLabelMap(datasource.getPluginId()); + }); + } + + /** + * Passes the payload to pluginExecutor post datasource validation and context retrieval + *

+ * This method validates the datasource, retrieves context and subsequently passes the payload to pluginExecutor for + * further execution of the request. + *

+ *

In case of failure the method retries to from context

+ * + * @param executeActionDTO + * @param actionDTO + * @param datasource + * @param plugin + * @param pluginExecutor + * @param environmentName + * @return actionExecutionResultMono + */ + protected Mono verifyDatasourceAndMakeRequest (ExecuteActionDTO executeActionDTO, + ActionDTO actionDTO, + Datasource datasource, + Plugin plugin, + PluginExecutor pluginExecutor, + String environmentName) { + // This method will be overridden in EE branch to make use of environmentName. + Mono validatedDatasourceMono = getValidatedDatasourceForActionExecution(datasource, environmentName); + + Mono executionMono = validatedDatasourceMono + .flatMap(datasource1 -> getDatasourceContextFromValidatedDatasourceForActionExecution(datasource1, + plugin, + environmentName)) + // Now that we have the context (connection details), execute the action. + .flatMap(resourceContext -> validatedDatasourceMono + .flatMap(datasource1 -> { + final Instant requestedAt = Instant.now(); + return ((Mono) pluginExecutor. + executeParameterized(resourceContext.getConnection(), + executeActionDTO, + datasource1.getDatasourceConfiguration(), + actionDTO.getActionConfiguration())) + .map(actionExecutionResult -> { + ActionExecutionRequest actionExecutionRequest = actionExecutionResult.getRequest(); + if (actionExecutionRequest == null) { + actionExecutionRequest = new ActionExecutionRequest(); + } + actionExecutionRequest.setActionId(executeActionDTO.getActionId()); + actionExecutionRequest.setRequestedAt(requestedAt); + + actionExecutionResult.setRequest(actionExecutionRequest); + return actionExecutionResult; + }); + })); + + return executionMono.onErrorResume(StaleConnectionException.class, error -> { + log.info("Looks like the connection is stale. Retrying with a fresh context."); + return deleteDatasourceContextForRetry(datasource, environmentName) + .then(executionMono); + }); + } + + /** + * Validates the datasource for further execution + * @param datasource + * @return + */ + protected Mono getValidatedDatasourceForActionExecution (Datasource datasource, String environmentName) { + // the environmentName argument is not consumed over here + // See EE override for usage of variable + return authenticationValidator.validateAuthentication(datasource).cache(); + } + + /** + * Provides datasource context for execution + * @param validatedDatasource + * @param plugin + * @param environmentName + * @return datasourceContextMono + */ + protected Mono> getDatasourceContextFromValidatedDatasourceForActionExecution + (Datasource validatedDatasource, Plugin plugin, String environmentName) { + // the environmentName argument is not consumed over here + // See EE override for usage of variable + if (plugin.isRemotePlugin()) { + return datasourceContextService.getRemoteDatasourceContext(plugin, validatedDatasource); + } + return datasourceContextService.getDatasourceContext(validatedDatasource); + + } + + /** + * Deletes the datasourceContext for the given datasource + * @param datasource + * @param environmentName + * @return datasourceContextMono + */ + protected Mono> deleteDatasourceContextForRetry(Datasource datasource, String environmentName) { + // the environmentName argument is not consumed over here + // See EE override for usage of variable + return datasourceContextService.deleteDatasourceContext(datasource.getId()); + } + + protected Mono handleExecutionErrors(Mono actionExecutionResultMono, + ActionDTO actionDTO, + Integer timeoutDuration, + String actionId) { + return actionExecutionResultMono + .onErrorMap(TimeoutException.class, error -> + new AppsmithPluginException(AppsmithPluginError.PLUGIN_QUERY_TIMEOUT_ERROR, + actionDTO.getName(), + timeoutDuration)) + .onErrorMap(StaleConnectionException.class, error -> + new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, + "Secondary stale connection error.")) + .onErrorResume(e -> { + log.debug("{}: In the action execution error mode.", + Thread.currentThread().getName(), e); + ActionExecutionResult result = new ActionExecutionResult(); + result.setBody(e.getMessage()); + result.setIsExecutionSuccess(false); + final ActionExecutionRequest actionExecutionRequest = new ActionExecutionRequest(); + actionExecutionRequest.setActionId(actionId); + actionExecutionRequest.setRequestedAt(Instant.now()); + result.setRequest(actionExecutionRequest); + // Set the status code for Appsmith plugin errors + if (e instanceof AppsmithPluginException) { + result.setStatusCode(((AppsmithPluginException) e).getAppErrorCode().toString()); + result.setTitle(((AppsmithPluginException) e).getTitle()); + result.setErrorType(((AppsmithPluginException) e).getErrorType()); + } else { + result.setStatusCode(AppsmithPluginError.PLUGIN_ERROR.getAppErrorCode().toString()); + + if (e instanceof AppsmithException) { + result.setTitle(((AppsmithException) e).getTitle()); + result.setErrorType(((AppsmithException) e).getErrorType()); + } + } + return Mono.just(result); + }); + + } + + /** + * Handles the execution logic, call to pluginExecutor with the payload post retrieval and validation of action, datasource, and plugin + * @param executeActionDTO + * @param actionMono + * @param actionDTOMono + * @param datasourceMono + * @param pluginMono + * @param pluginExecutorMono + * @param actionName + * @param actionId + * @param environmentName + * @return actionExecutionResultMono + */ + protected Mono getActionExecutionResult( ExecuteActionDTO executeActionDTO, + Mono actionMono, + Mono actionDTOMono, + Mono datasourceMono, + Mono pluginMono, + Mono pluginExecutorMono, + AtomicReference actionName, + String actionId, + String environmentName) { + + Mono> executeActionPublishersCache = + Mono.zip(actionDTOMono, datasourceMono, pluginExecutorMono, pluginMono, actionMono).cache(); + + return executeActionPublishersCache .flatMap(tuple -> { - final ActionDTO action = tuple.getT1(); + final ActionDTO actionDTO = tuple.getT1(); final Datasource datasource = tuple.getT2(); final PluginExecutor pluginExecutor = tuple.getT3(); final Plugin plugin = tuple.getT4(); + final NewAction actionFromDb = tuple.getT5(); // Set the action name - actionName.set(action.getName()); - - ActionConfiguration actionConfiguration = action.getActionConfiguration(); - - Integer timeoutDuration = actionConfiguration.getTimeoutInMillisecond(); + actionName.set(actionDTO.getName()); log.debug("[{}]Execute Action called in Page {}, for action id : {} action name : {}", Thread.currentThread().getName(), - action.getPageId(), actionId, action.getName()); + actionDTO.getPageId(), actionId, actionDTO.getName()); - Mono validatedDatasourceMono = authenticationValidator.validateAuthentication(datasource).cache(); + Integer timeoutDuration = actionDTO.getActionConfiguration().getTimeoutInMillisecond(); - Mono executionMono = validatedDatasourceMono - .flatMap(datasource1 -> { - if (plugin.isRemotePlugin()) { - return datasourceContextService.getRemoteDatasourceContext(plugin, datasource1); - } else { - return datasourceContextService.getDatasourceContext(datasource1); - } - }) - // Now that we have the context (connection details), execute the action. - .flatMap(resourceContext -> validatedDatasourceMono - .flatMap(datasource1 -> { - final Instant requestedAt = Instant.now(); - return ((Mono) pluginExecutor.executeParameterized( - resourceContext.getConnection(), - executeActionDTO, - datasource1.getDatasourceConfiguration(), - actionConfiguration - )).map(actionExecutionResult -> { - ActionExecutionRequest actionExecutionRequest = actionExecutionResult.getRequest(); - if (actionExecutionRequest == null) { - actionExecutionRequest = new ActionExecutionRequest(); - } - actionExecutionRequest.setActionId(actionId); - actionExecutionRequest.setRequestedAt(requestedAt); + Mono actionExecutionResultMono = + verifyDatasourceAndMakeRequest(executeActionDTO, actionDTO, datasource, + plugin, pluginExecutor, environmentName) + .timeout(Duration.ofMillis(timeoutDuration)); - actionExecutionResult.setRequest(actionExecutionRequest); - - return actionExecutionResult; - }); - }) - ); - - return executionMono - .onErrorResume(StaleConnectionException.class, error -> { - log.info("Looks like the connection is stale. Retrying with a fresh context."); - return datasourceContextService - .deleteDatasourceContext(datasource.getId()) - .then(executionMono); - }) - .timeout(Duration.ofMillis(timeoutDuration)) - .onErrorMap(TimeoutException.class, - error -> new AppsmithPluginException( - AppsmithPluginError.PLUGIN_QUERY_TIMEOUT_ERROR, - action.getName(), timeoutDuration - ) - ) - .onErrorMap( - StaleConnectionException.class, - error -> new AppsmithPluginException( - AppsmithPluginError.PLUGIN_ERROR, - "Secondary stale connection error." - ) - ) - .onErrorResume(e -> { - log.debug("{}: In the action execution error mode.", - Thread.currentThread().getName(), e); - ActionExecutionResult result = new ActionExecutionResult(); - result.setBody(e.getMessage()); - result.setIsExecutionSuccess(false); - final ActionExecutionRequest actionExecutionRequest = new ActionExecutionRequest(); - actionExecutionRequest.setActionId(actionId); - actionExecutionRequest.setRequestedAt(Instant.now()); - result.setRequest(actionExecutionRequest); - // Set the status code for Appsmith plugin errors - if (e instanceof AppsmithPluginException) { - result.setStatusCode(((AppsmithPluginException) e).getAppErrorCode().toString()); - result.setTitle(((AppsmithPluginException) e).getTitle()); - result.setErrorType(((AppsmithPluginException) e).getErrorType()); - } else { - result.setStatusCode(AppsmithPluginError.PLUGIN_ERROR.getAppErrorCode().toString()); - - if (e instanceof AppsmithException) { - result.setTitle(((AppsmithException) e).getTitle()); - result.setErrorType(((AppsmithException) e).getErrorType()); - } - } - return Mono.just(result); - }) + return handleExecutionErrors(actionExecutionResultMono, actionDTO ,timeoutDuration ,actionId) .elapsed() // Now send the analytics event for this execution .flatMap(tuple1 -> { @@ -806,18 +940,10 @@ public class NewActionServiceCEImpl extends BaseService { - ActionExecutionResult actionExecutionResult = result; - NewAction actionFromDb = tuple2.getT1(); - ActionDTO actionDTO = tuple2.getT2(); - Datasource datasourceFromDb = tuple2.getT3(); - - return Mono.when(sendExecuteAnalyticsEvent(actionFromDb, actionDTO, datasourceFromDb, executeActionDTO, actionExecutionResult, timeElapsed)) - .thenReturn(result); - }); - } - ); + return sendExecuteAnalyticsEvent(actionFromDb, actionDTO, datasource, + executeActionDTO, result, timeElapsed) + .then(Mono.just(result)); + }); }) .onErrorResume(AppsmithException.class, error -> { ActionExecutionResult result = new ActionExecutionResult(); @@ -828,40 +954,63 @@ public class NewActionServiceCEImpl extends BaseService editorConfigLabelMapMono = datasourceMono - .flatMap(datasource -> { - if (datasource.getId() != null) { - return pluginService.getEditorConfigLabelMap(datasource.getPluginId()); - } + /** + * Fetches the required Mono (action, datasource, and plugin) and makes actionExecution call to plugin + * @param executeActionDTO + * @param environmentName + * @return actionExecutionResult if query succeeds, error messages otherwise + */ + public Mono executeAction(ExecuteActionDTO executeActionDTO, String environmentName) { - return Mono.just(new HashMap()); - }); + // 1. Validate input parameters which are required for mustache replacements + replaceNullWithQuotesForParamValues(executeActionDTO.getParams()); - return Mono.zip(actionExecutionResultMono, editorConfigLabelMapMono) - .flatMap(tuple -> { - ActionExecutionResult result = tuple.getT1(); - // In case the action was executed in view mode, do not return the request object + String actionId = executeActionDTO.getActionId(); + AtomicReference actionName = new AtomicReference<>(); + actionName.set(""); + + // 2. Fetch the action from the DB and check if it can be executed + Mono actionMono = getCachedActionForActionExecution(actionId); + Mono actionDTOMono = getCachedActionDTOForActionExecution(actionMono, executeActionDTO, actionId); + + // 3. Instantiate the implementation class based on the query type + Mono datasourceMono = getCachedDatasourceForActionExecution(actionDTOMono); + Mono pluginMono = getCachedPluginForActionExecution(datasourceMono, actionId); + Mono pluginExecutorMono = pluginExecutorHelper.getPluginExecutor(pluginMono); + + // 4. Execute the query + Mono actionExecutionResultMono = getActionExecutionResult(executeActionDTO, + actionMono, + actionDTOMono, + datasourceMono, + pluginMono, + pluginExecutorMono, + actionName, + actionId, + environmentName); + + Mono editorConfigLabelMapMono = getEditorConfigLabelMap(datasourceMono); + + return actionExecutionResultMono + .zipWith(editorConfigLabelMapMono, (result, labelMap) -> { if (TRUE.equals(executeActionDTO.getViewMode())) { result.setRequest(null); - return Mono.just(result); + } else if (result.getRequest() != null && result.getRequest().getRequestParams()!= null) { + transformRequestParams(result, labelMap); } - - if (result.getRequest() == null || result.getRequest().getRequestParams() == null) { - return Mono.just(result); - } - - Map labelMap = tuple.getT2(); - transformRequestParams(result, labelMap); - - return Mono.just(result); + return result; }) .map(result -> addDataTypesAndSetSuggestedWidget(result, executeActionDTO.getViewMode())); } - @Override - public Mono executeAction(Flux partFlux, String branchName) { - + /** + * Creates the ExecuteActionDTO from Flux of ByteBuffers + * @param partFlux + * @return an executionDTO object with parameterMap + */ + protected Mono createExecuteActionDTO(Flux partFlux) { final ExecuteActionDTO dto = new ExecuteActionDTO(); return partFlux .flatMap(part -> { @@ -933,22 +1082,31 @@ public class NewActionServiceCEImpl extends BaseService { String pseudoBindingName = param.getPseudoBindingName(); - param.setKey(dto.getInvertParameterMap().get(pseudoBindingName)); + param.setKey(dto.getInvertParameterMap() + .get(pseudoBindingName)); //if the type is not an array e.g. "k1": "string" or "k1": "boolean" - if (dto.getParamProperties().get(pseudoBindingName) instanceof String) { - param.setClientDataType(ClientDataType.valueOf(String.valueOf(dto.getParamProperties().get(pseudoBindingName)).toUpperCase())); - } else if (dto.getParamProperties().get(pseudoBindingName) instanceof LinkedHashMap) { + if (dto.getParamProperties() + .get(pseudoBindingName) instanceof String) { + param.setClientDataType(ClientDataType.valueOf(String.valueOf(dto.getParamProperties() + .get(pseudoBindingName)) + .toUpperCase())); + } else if (dto.getParamProperties() + .get(pseudoBindingName) instanceof LinkedHashMap) { //if the type is an array e.g. "k1": { "array": [ "string", "number", "string", "boolean"] LinkedHashMap stringArrayListLinkedHashMap = - (LinkedHashMap) dto.getParamProperties().get(pseudoBindingName); - Optional firstKeyOpt = stringArrayListLinkedHashMap.keySet().stream().findFirst(); + (LinkedHashMap) dto.getParamProperties() + .get(pseudoBindingName); + Optional firstKeyOpt = stringArrayListLinkedHashMap.keySet() + .stream() + .findFirst(); if (firstKeyOpt.isPresent()) { String firstKey = firstKeyOpt.get(); param.setClientDataType(ClientDataType.valueOf(firstKey.toUpperCase())); List individualTypes = stringArrayListLinkedHashMap.get(firstKey); List dataTypesOfArrayElements = individualTypes.stream() - .map(it -> ClientDataType.valueOf(String.valueOf(it).toUpperCase())) + .map(it -> ClientDataType.valueOf(String.valueOf(it) + .toUpperCase())) .collect(Collectors.toList()); param.setDataTypesOfArrayElements(dataTypesOfArrayElements); } @@ -958,17 +1116,30 @@ public class NewActionServiceCEImpl extends BaseService this - .findByBranchNameAndDefaultActionId(branchName, executeActionDTO.getActionId(), actionPermission.getExecutePermission()) + }); + } + + /** + * Executes the action(queries) by creating executeActionDTO and sending it to the plugin for further execution + * @param partFlux + * @param branchName + * @param environmentName + * @return Mono of actionExecutionResult if the query succeeds, error messages otherwise + */ + @Override + public Mono executeAction(Flux partFlux, String branchName, String environmentName) { + return createExecuteActionDTO(partFlux) + .flatMap(executeActionDTO -> findByBranchNameAndDefaultActionId(branchName, + executeActionDTO.getActionId(), + actionPermission.getExecutePermission()) .map(branchedAction -> { executeActionDTO.setActionId(branchedAction.getId()); return executeActionDTO; - }) - ) - .flatMap(this::executeAction); + })) + .flatMap(executeActionDTO -> this.executeAction(executeActionDTO, environmentName)); } + @Override public Mono getValidActionForExecution(ExecuteActionDTO executeActionDTO, String actionId, NewAction newAction) { Mono actionDTOMono = Mono.just(newAction) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java index 90401df571..09b560eabb 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java @@ -839,7 +839,7 @@ public class ActionServiceCE_Test { Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -889,7 +889,7 @@ public class ActionServiceCE_Test { Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -933,7 +933,7 @@ public class ActionServiceCE_Test { .thenReturn(Mono.error(new StaleConnectionException())).thenReturn(Mono.error(new StaleConnectionException())); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -977,7 +977,7 @@ public class ActionServiceCE_Test { .thenAnswer(x -> Mono.delay(Duration.ofMillis(1000)).ofType(ActionExecutionResult.class)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -1079,7 +1079,7 @@ public class ActionServiceCE_Test { executeActionDTO.setActionId(createdAction.getId()); executeActionDTO.setViewMode(false); - Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO); + Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(actionExecutionResultMono) .assertNext(result -> { @@ -1114,7 +1114,7 @@ public class ActionServiceCE_Test { Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.just(mockResult)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO); + Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO, null); return actionExecutionResultMono; } @@ -1181,7 +1181,7 @@ public class ActionServiceCE_Test { ExecuteActionDTO executeActionDTO = new ExecuteActionDTO(); executeActionDTO.setActionId(savedAction.getId()); executeActionDTO.setViewMode(false); - return newActionService.executeAction(executeActionDTO); + return newActionService.executeAction(executeActionDTO, null); }); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java index ad68a8d117..2070dd0d1b 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java @@ -1,5 +1,6 @@ package com.appsmith.server.services.ce; +import com.appsmith.external.dtos.ExecuteActionDTO; import com.appsmith.external.models.ActionExecutionResult; import com.appsmith.external.models.Datasource; import com.appsmith.server.acl.PolicyGenerator; @@ -198,7 +199,7 @@ public class NewActionServiceCEImplTest { @Test public void testExecuteAction_withoutExecuteActionDTOPart_failsValidation() { - final Mono actionExecutionResultMono = newActionService.executeAction(Flux.empty(), null); + final Mono actionExecutionResultMono = newActionService.executeAction(Flux.empty(), null, null); StepVerifier .create(actionExecutionResultMono) @@ -219,7 +220,7 @@ public class NewActionServiceCEImplTest { final Flux partsFlux = BodyExtractors.toParts() .extract(mock, this.context); - final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null); + final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null, null); StepVerifier .create(actionExecutionResultMono) @@ -240,7 +241,7 @@ public class NewActionServiceCEImplTest { final Flux partsFlux = BodyExtractors.toParts() .extract(mock, this.context); - final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null); + final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null, null); StepVerifier .create(actionExecutionResultMono) @@ -330,7 +331,7 @@ public class NewActionServiceCEImplTest { NewActionServiceCE newActionServiceSpy = spy(newActionService); - Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null); + Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null, null); ActionExecutionResult mockResult = new ActionExecutionResult(); mockResult.setIsExecutionSuccess(true); @@ -339,7 +340,7 @@ public class NewActionServiceCEImplTest { NewAction newAction = new NewAction(); newAction.setId("63285a3388e48972c7519b18"); - doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any()); + doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any(), any()); doReturn(Mono.just(newAction)).when(newActionServiceSpy).findByBranchNameAndDefaultActionId(any(), any(), any()); @@ -380,7 +381,7 @@ public class NewActionServiceCEImplTest { NewActionServiceCE newActionServiceSpy = spy(newActionService); - Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null); + Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null, null); ActionExecutionResult mockResult = new ActionExecutionResult(); mockResult.setIsExecutionSuccess(true); @@ -389,7 +390,7 @@ public class NewActionServiceCEImplTest { NewAction newAction = new NewAction(); newAction.setId("63285a3388e48972c7519b18"); - doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any()); + doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any(), any()); doReturn(Mono.just(newAction)).when(newActionServiceSpy).findByBranchNameAndDefaultActionId(any(), any(), any());