diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java index 3badfd9a93..84656b36cf 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java @@ -13,5 +13,9 @@ public final class ActionSpans { public static final String ACTION_EXECUTION_VALIDATE_AUTHENTICATION = "validate.authentication"; public static final String ACTION_EXECUTION_PLUGIN_EXECUTION = "total.plugin.execution"; public static final String ACTION_EXECUTION_SERVER_EXECUTION = "total.server.execution"; + public static final String GET_UNPUBLISHED_ACTION = "get.action.unpublished"; + public static final String GET_VIEW_MODE_ACTION = "get.action.viewmode"; + public static final String GET_ACTION_REPOSITORY_CALL = "get.action.repository.call"; + } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java index e237569b00..415a2bd20c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java @@ -36,8 +36,9 @@ public interface CustomNewActionRepositoryCE extends AppsmithRepository findAllActionsByNameAndPageIdsAndViewMode(String name, List pageIds, Boolean viewMode, AclPermission aclPermission, Sort sort); - Flux findUnpublishedActionsByNameInAndPageIdAndExecuteOnLoadTrue( - Set names, String pageId, AclPermission permission); + Flux findUnpublishedActionsByNameInAndPageIdAndExecuteOnLoadTrue(Set names, + String pageId, + AclPermission permission); Flux findByApplicationId(String applicationId, AclPermission aclPermission, Sort sort); @@ -56,4 +57,10 @@ public interface CustomNewActionRepositoryCE extends AppsmithRepository findByListOfPageIds(List pageIds, AclPermission permission); Flux findByListOfPageIds(List pageIds, Optional permission); + Flux findNonJsActionsByApplicationIdAndViewMode(String applicationId, Boolean viewMode, AclPermission aclPermission); + Flux findAllNonJsActionsByNameAndPageIdsAndViewMode(String name, List pageIds, + Boolean viewMode, + AclPermission aclPermission, + Sort sort); + } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java index 7695ff125f..96f6ef697e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java @@ -1,5 +1,6 @@ package com.appsmith.server.repositories.ce; +import com.appsmith.external.models.PluginType; import com.appsmith.external.models.QActionConfiguration; import com.appsmith.external.models.QBranchAwareDomain; import com.appsmith.server.acl.AclPermission; @@ -319,7 +320,7 @@ public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl< Criteria gitSyncIdCriteria = where(FieldName.GIT_SYNC_ID).is(gitSyncId); return queryFirst(List.of(defaultAppIdCriteria, gitSyncIdCriteria), permission); } - + @Override public Flux findByListOfPageIds(List pageIds, AclPermission permission) { @@ -336,4 +337,68 @@ public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl< return queryAll(List.of(pageIdCriteria), permission); } + + @Override + public Flux findNonJsActionsByApplicationIdAndViewMode(String applicationId, Boolean viewMode, + AclPermission aclPermission) { + List criteria = new ArrayList<>(); + + Criteria applicationCriterion = where(fieldName(QNewAction.newAction.applicationId)).is(applicationId); + criteria.add(applicationCriterion); + + Criteria nonJsTypeCriteria = where(fieldName(QNewAction.newAction.pluginType)).ne(PluginType.JS); + criteria.add(nonJsTypeCriteria); + + if (Boolean.FALSE.equals(viewMode)) { + // In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions + Criteria deletedCriterion = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null); + criteria.add(deletedCriterion); + } + + return queryAll(criteria, aclPermission); + } + + @Override + public Flux findAllNonJsActionsByNameAndPageIdsAndViewMode(String name, List pageIds, + Boolean viewMode, AclPermission aclPermission, + Sort sort) { + List criteriaList = new ArrayList<>(); + + Criteria nonJsTypeCriteria = where(fieldName(QNewAction.newAction.pluginType)).ne(PluginType.JS); + criteriaList.add(nonJsTypeCriteria); + + // Fetch published actions + if (Boolean.TRUE.equals(viewMode)) { + + if (name != null) { + Criteria nameCriteria = where(fieldName(QNewAction.newAction.publishedAction) + "." + fieldName(QNewAction.newAction.publishedAction.name)).is(name); + criteriaList.add(nameCriteria); + } + + if (pageIds != null && !pageIds.isEmpty()) { + Criteria pageCriteria = where(fieldName(QNewAction.newAction.publishedAction) + "." + fieldName(QNewAction.newAction.publishedAction.pageId)).in(pageIds); + criteriaList.add(pageCriteria); + } + + } + // Fetch unpublished actions + else { + + if (name != null) { + Criteria nameCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.name)).is(name); + criteriaList.add(nameCriteria); + } + + if (pageIds != null && !pageIds.isEmpty()) { + Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).in(pageIds); + criteriaList.add(pageCriteria); + } + + // In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions + Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null); + criteriaList.add(deletedCriteria); + } + + return queryAll(criteriaList, aclPermission, sort); + } } 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 2a9f9adf68..820963760c 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 @@ -73,6 +73,10 @@ public interface NewActionServiceCE extends CrudService { Mono deleteUnpublishedAction(String id); + Flux getUnpublishedActions(MultiValueMap params, Boolean includeJsActions); + + Flux getUnpublishedActions(MultiValueMap params, String branchName, Boolean includeJsActions); + Flux getUnpublishedActions(MultiValueMap params); Flux getUnpublishedActions(MultiValueMap params, String branchName); 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 0e6d3865c5..46b58361ac 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 @@ -127,6 +127,9 @@ import static com.appsmith.external.constants.spans.ActionSpans.ACTION_EXECUTION import static com.appsmith.external.constants.spans.ActionSpans.ACTION_EXECUTION_REQUEST_PARSING; import static com.appsmith.external.constants.spans.ActionSpans.ACTION_EXECUTION_SERVER_EXECUTION; import static com.appsmith.external.constants.spans.ActionSpans.ACTION_EXECUTION_VALIDATE_AUTHENTICATION; +import static com.appsmith.external.constants.spans.ActionSpans.GET_ACTION_REPOSITORY_CALL; +import static com.appsmith.external.constants.spans.ActionSpans.GET_UNPUBLISHED_ACTION; +import static com.appsmith.external.constants.spans.ActionSpans.GET_VIEW_MODE_ACTION; import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject; import static com.appsmith.external.helpers.DataTypeStringUtils.getDisplayDataTypes; import static com.appsmith.external.helpers.PluginUtils.setValueSafelyInFormData; @@ -178,6 +181,8 @@ public class NewActionServiceCEImpl extends BaseService defaultPluginMap = new HashMap<>(); + private final AtomicReference jsTypePluginReference = new AtomicReference<>(); public NewActionServiceCEImpl(Scheduler scheduler, Validator validator, @@ -1513,14 +1518,17 @@ public class NewActionServiceCEImpl extends BaseService getActionsForViewMode(String defaultApplicationId, String branchName) { return applicationService.findBranchedApplicationId(branchName, defaultApplicationId, applicationPermission.getReadPermission()) .flatMapMany(this::getActionsForViewMode) - .map(responseUtils::updateActionViewDTOWithDefaultResources); + .map(responseUtils::updateActionViewDTOWithDefaultResources) + .name(GET_VIEW_MODE_ACTION) + .tap(Micrometer.observation(observationRegistry)); } @Override @@ -1711,7 +1719,7 @@ public class NewActionServiceCEImpl extends BaseService getUnpublishedActions(MultiValueMap params) { + public Flux getUnpublishedActions(MultiValueMap params, Boolean includeJsActions) { String name = null; List pageIds = new ArrayList<>(); @@ -1726,22 +1734,50 @@ public class NewActionServiceCEImpl extends BaseService actionsFromRepository; + if (params.getFirst(FieldName.APPLICATION_ID) != null) { // Fetch unpublished pages because GET actions is only called during edit mode. For view mode, different // function call is made which takes care of returning only the essential fields of an action - return applicationService - .findById(params.getFirst(FieldName.APPLICATION_ID), applicationPermission.getReadPermission()) - .flatMapMany(application -> repository.findByApplicationIdAndViewMode(application.getId(), false, actionPermission.getReadPermission())) - .flatMap(this::sanitizeAction) - .flatMap(this::setTransientFieldsInUnpublishedAction); + + if (FALSE.equals(includeJsActions)) { + actionsFromRepository = repository.findNonJsActionsByApplicationIdAndViewMode(params.getFirst(FieldName.APPLICATION_ID), + false, + actionPermission.getReadPermission()); + + } else { + actionsFromRepository = repository.findByApplicationIdAndViewMode(params.getFirst(FieldName.APPLICATION_ID), + false, + actionPermission.getReadPermission()); + } + + } else { + + if (FALSE.equals(includeJsActions)) { + actionsFromRepository = repository.findAllNonJsActionsByNameAndPageIdsAndViewMode(name, pageIds, false, + actionPermission.getReadPermission(), + sort); + } else { + actionsFromRepository = repository.findAllActionsByNameAndPageIdsAndViewMode(name, pageIds, false, + actionPermission.getReadPermission(), + sort); + } } - return repository.findAllActionsByNameAndPageIdsAndViewMode(name, pageIds, false, actionPermission.getReadPermission(), sort) - .flatMap(this::sanitizeAction) - .flatMap(this::setTransientFieldsInUnpublishedAction); + + return actionsFromRepository + .collectList() + .flatMapMany(this::addMissingPluginDetailsIntoAllActions) + .flatMap(this::setTransientFieldsInUnpublishedAction) + // this generates four different tags, (ApplicationId, FieldId) *(True, False) + .tag("includeJsAction", (params.get(FieldName.APPLICATION_ID) == null ? FieldName.PAGE_ID: FieldName.APPLICATION_ID )+ includeJsActions.toString()) + .name(GET_ACTION_REPOSITORY_CALL) + .tap(Micrometer.observation(observationRegistry)); } @Override - public Flux getUnpublishedActions(MultiValueMap params, String branchName) { + public Flux getUnpublishedActions(MultiValueMap params, + String branchName, + Boolean includeJsActions) { MultiValueMap updatedParams = new LinkedMultiValueMap<>(params); // Get branched applicationId and pageId @@ -1762,21 +1798,33 @@ public class NewActionServiceCEImpl extends BaseService getUnpublishedActions(MultiValueMap params) { + return getUnpublishedActions(params, TRUE); + } + + @Override + public Flux getUnpublishedActions(MultiValueMap params, String branchName) { + return getUnpublishedActions(params, branchName, TRUE); + } + @Override public Flux getUnpublishedActionsExceptJs(MultiValueMap params) { - return this.getUnpublishedActions(params) + return this.getUnpublishedActions(params, FALSE) .filter(actionDTO -> !PluginType.JS.equals(actionDTO.getPluginType())); } @Override public Flux getUnpublishedActionsExceptJs(MultiValueMap params, String branchName) { - return this.getUnpublishedActions(params, branchName) - .filter(actionDTO -> !PluginType.JS.equals(actionDTO.getPluginType())); + return this.getUnpublishedActions(params, branchName, FALSE) + .filter(actionDTO -> !PluginType.JS.equals(actionDTO.getPluginType())) + .name(GET_UNPUBLISHED_ACTION) + .tap(Micrometer.observation(observationRegistry)); } /** @@ -1797,6 +1845,66 @@ public class NewActionServiceCEImpl extends BaseService addMissingPluginDetailsIntoAllActions(List actionList) { + + Mono> pluginMapMono = Mono.just(defaultPluginMap); + + /* This conditional would be false once per pod per restart, as soon as first request goes through + the default plugin map will have all the plugins and subsequent requests might not require to fetch again. + */ + + if (CollectionUtils.isEmpty(defaultPluginMap)) { + pluginMapMono = pluginService.getDefaultPlugins() + .collectMap(Plugin::getId) + .map(pluginMap -> { + pluginMap.forEach((pluginId, plugin) -> { + defaultPluginMap.put(pluginId, plugin); + if (JS_PLUGIN_PACKAGE_NAME + .equals(plugin.getPackageName())) { + jsTypePluginReference.set(plugin); + } + }); + return pluginMap; + }); + } + + return pluginMapMono + .thenMany(Flux.fromIterable(actionList)) + .flatMap(action-> { + if (!isPluginTypeOrPluginIdMissing(action)) { + return Mono.just(action); + } + return addMissingPluginDetailsToNewActionObjects(action); + }); + } + + private Mono addMissingPluginDetailsToNewActionObjects(NewAction action) { + ActionDTO actionDTO = action.getUnpublishedAction(); + if (actionDTO == null) { + return Mono.just(action); + } + + Datasource datasource = actionDTO.getDatasource(); + if (actionDTO.getCollectionId() != null) { + action.setPluginType(JS_PLUGIN_TYPE); + action.setPluginId(jsTypePluginReference.get().getId()); + return Mono.just(action); + + } else if (datasource != null && datasource.getPluginId() != null) { + String pluginId = datasource.getPluginId(); + action.setPluginId(pluginId); + + if (defaultPluginMap.containsKey(pluginId)) { + Plugin plugin = defaultPluginMap.get(pluginId); + action.setPluginType(plugin.getType()); + } else { + setPluginTypeFromId(action, pluginId); + } + } + + return Mono.just(action); + } + @Override public Mono fillSelfReferencingDataPaths(ActionDTO actionDTO) { Mono pluginMono = pluginService.getById(actionDTO.getPluginId());