diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java index 65c5bd5414..cad67d731e 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java @@ -11,7 +11,7 @@ public interface PluginConstants { String AMAZON_S3_PLUGIN = "amazons3-plugin"; String GOOGLE_SHEETS_PLUGIN = "google-sheets-plugin"; String REST_API_PLUGIN = "restapi-plugin"; - String GRAPH_QL_PLUGIN = "graphql-plugin"; + String GRAPHQL_PLUGIN = "graphql-plugin"; String OPEN_AI_PLUGIN = "openai-plugin"; String ANTHROPIC_PLUGIN = "anthropic-plugin"; String GOOGLE_AI_PLUGIN = "googleai-plugin"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java index 60f36fe6e6..af75aa2a1c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java @@ -60,6 +60,7 @@ import static com.appsmith.server.constants.Url.TENANT_URL; import static com.appsmith.server.constants.Url.THEME_URL; import static com.appsmith.server.constants.Url.USAGE_PULSE_URL; import static com.appsmith.server.constants.Url.USER_URL; +import static com.appsmith.server.constants.ce.UrlCE.CONSOLIDATED_API_URL; import static java.time.temporal.ChronoUnit.DAYS; @EnableWebFluxSecurity @@ -211,7 +212,8 @@ public class SecurityConfig { ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, USER_URL + "/resendEmailVerification"), ServerWebExchangeMatchers.pathMatchers( HttpMethod.POST, USER_URL + "/verifyEmailVerificationToken"), - ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, PRODUCT_ALERT + "/alert")) + ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, PRODUCT_ALERT + "/alert"), + ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, CONSOLIDATED_API_URL)) .permitAll() .pathMatchers("/public/**", "/oauth2/**") .permitAll() diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java index b02a506d6a..c9c8c262d1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java @@ -37,6 +37,7 @@ public class UrlCE { public static final String CUSTOM_JS_LIB_URL = BASE_URL + VERSION + "/libraries"; public static final String PRODUCT_ALERT = BASE_URL + VERSION + "/product-alert"; public static final String SEARCH_ENTITY_URL = BASE_URL + VERSION + "/search-entities"; + public static final String CONSOLIDATED_API_URL = BASE_URL + VERSION + "/consolidated-api"; // Sub-paths public static final String MOCKS = "/mocks"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ConsolidatedAPIController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ConsolidatedAPIController.java new file mode 100644 index 0000000000..bb213d5744 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ConsolidatedAPIController.java @@ -0,0 +1,54 @@ +package com.appsmith.server.controllers; + +import com.appsmith.external.views.Views; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.constants.Url; +import com.appsmith.server.domains.ApplicationMode; +import com.appsmith.server.dtos.ConsolidatedAPIResponseDTO; +import com.appsmith.server.dtos.ResponseDTO; +import com.appsmith.server.services.ConsolidatedAPIService; +import com.fasterxml.jackson.annotation.JsonView; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +@Slf4j +@RestController +@RequestMapping(Url.CONSOLIDATED_API_URL) +public class ConsolidatedAPIController { + private final ConsolidatedAPIService consolidatedAPIService; + + public ConsolidatedAPIController(ConsolidatedAPIService consolidatedAPIService) { + this.consolidatedAPIService = consolidatedAPIService; + } + + /** + * This endpoint is meant to be used by the client application at the time of 1st page load. Client currently makes + * several API calls to fetch all the required data. This endpoint consolidates all that data and returns them as + * response hence enabling the client to fetch the required data via a single API call only. + */ + @JsonView(Views.Public.class) + @GetMapping + public Mono> getAllDataForFirstPageLoad( + @RequestParam(required = false) String applicationId, + @RequestParam(required = false) String defaultPageId, + @RequestParam(required = false) ApplicationMode mode, + @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { + log.debug( + "Going to fetch consolidatedAPI response for applicationId: {}, defaultPageId: {}, branchName: {}, " + + "mode: {}", + applicationId, + defaultPageId, + branchName, + mode); + return consolidatedAPIService + .getConsolidatedInfoForPageLoad(defaultPageId, applicationId, branchName, mode) + .map(consolidatedAPIResponseDTO -> + new ResponseDTO<>(HttpStatus.OK.value(), consolidatedAPIResponseDTO, null)); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ConsolidatedAPIResponseDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ConsolidatedAPIResponseDTO.java new file mode 100644 index 0000000000..4e8062fcee --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ConsolidatedAPIResponseDTO.java @@ -0,0 +1,79 @@ +package com.appsmith.server.dtos; + +import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.Datasource; +import com.appsmith.server.domains.CustomJSLib; +import com.appsmith.server.domains.Plugin; +import com.appsmith.server.domains.Tenant; +import com.appsmith.server.domains.Theme; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +/** + * This class serves as a DTO for the response data returned via the consolidated API endpoint call + * (v1/consolidated-api) . Each identifier in the class represents the data returned from a unique endpoint. The + * endpoint info is mentioned on top of each identifier. + */ +@Getter +@Setter +public class ConsolidatedAPIResponseDTO { + /* v1/users/me */ + ResponseDTO v1UsersMeResp; + + /* v1/users/features */ + ResponseDTO> v1UsersFeaturesResp; + + /* v1/tenants/current */ + ResponseDTO v1TenantsCurrentResp; + + /* v1/product-alert/alert */ + ResponseDTO v1ProductAlertResp; + + /* v1/pages */ + ResponseDTO v1PagesResp; + + /* v1/actions/view */ + ResponseDTO> v1ActionsViewResp; + + /* v1/actions */ + ResponseDTO> v1ActionsResp; + + /* v1/collections/actions/view */ + ResponseDTO> v1CollectionsActionsViewResp; + + /* v1/collections/actions */ + ResponseDTO> v1CollectionsActionsResp; + + /* v1/themes/applications/{applicationId}/current */ + ResponseDTO v1ThemesApplicationCurrentModeResp; + + /* v1/themes/applications/{applicationId} */ + ResponseDTO> v1ThemesResp; + + /* v1/pages/{pageId}/view */ + ResponseDTO v1PublishedPageResp; + + /* v1/pages/{pageId} */ + ResponseDTO v1PageResp; + + /* v1/pages/{pageId} - for all pages */ + ResponseDTO> v1PageDSLs; + + /* v1/libraries/{applicationId}/view */ + ResponseDTO> v1LibrariesApplicationResp; + + /* v1/plugins */ + ResponseDTO> v1PluginsResp; + + /* v1/datasources */ + ResponseDTO> v1DatasourcesResp; + + /* v1/plugins/{pluginId}/form - for all plugins used in app */ + ResponseDTO> v1PluginFormConfigsResp; + + /* v1/datasources/mock */ + ResponseDTO> v1DatasourcesMockResp; +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConsolidatedAPIService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConsolidatedAPIService.java new file mode 100644 index 0000000000..698725f28d --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConsolidatedAPIService.java @@ -0,0 +1,10 @@ +package com.appsmith.server.services; + +import com.appsmith.server.domains.ApplicationMode; +import com.appsmith.server.dtos.ConsolidatedAPIResponseDTO; +import reactor.core.publisher.Mono; + +public interface ConsolidatedAPIService { + Mono getConsolidatedInfoForPageLoad( + String defaultPageId, String applicationId, String branchName, ApplicationMode mode); +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConsolidatedAPIServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConsolidatedAPIServiceImpl.java new file mode 100644 index 0000000000..8e75009206 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConsolidatedAPIServiceImpl.java @@ -0,0 +1,501 @@ +package com.appsmith.server.services; + +import com.appsmith.external.exceptions.ErrorDTO; +import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; +import com.appsmith.external.models.Datasource; +import com.appsmith.server.actioncollections.base.ActionCollectionService; +import com.appsmith.server.applications.base.ApplicationService; +import com.appsmith.server.datasources.base.DatasourceService; +import com.appsmith.server.domains.ApplicationMode; +import com.appsmith.server.domains.CustomJSLib; +import com.appsmith.server.domains.Plugin; +import com.appsmith.server.domains.Tenant; +import com.appsmith.server.domains.Theme; +import com.appsmith.server.dtos.ActionCollectionDTO; +import com.appsmith.server.dtos.ActionCollectionViewDTO; +import com.appsmith.server.dtos.ActionViewDTO; +import com.appsmith.server.dtos.ApplicationPagesDTO; +import com.appsmith.server.dtos.ConsolidatedAPIResponseDTO; +import com.appsmith.server.dtos.MockDataDTO; +import com.appsmith.server.dtos.MockDataSet; +import com.appsmith.server.dtos.PageDTO; +import com.appsmith.server.dtos.ProductAlertResponseDTO; +import com.appsmith.server.dtos.ResponseDTO; +import com.appsmith.server.dtos.UserProfileDTO; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.jslibs.base.CustomJSLibService; +import com.appsmith.server.newactions.base.NewActionService; +import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.plugins.base.PluginService; +import com.appsmith.server.themes.base.ThemeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.appsmith.external.constants.PluginConstants.PackageName.GRAPHQL_PLUGIN; +import static com.appsmith.external.constants.PluginConstants.PackageName.REST_API_PLUGIN; +import static com.appsmith.server.constants.ce.FieldNameCE.APPLICATION_ID; +import static com.appsmith.server.constants.ce.FieldNameCE.APP_MODE; +import static com.appsmith.server.constants.ce.FieldNameCE.WORKSPACE_ID; +import static org.apache.commons.lang3.StringUtils.isBlank; + +@Slf4j +@Service +public class ConsolidatedAPIServiceImpl implements ConsolidatedAPIService { + private static final String FEATURE_FLAG_RELEASE_SERVER_DSL_MIGRATIONS_ENABLED = + "release_server_dsl_migrations_enabled"; + public static final int INTERNAL_SERVER_ERROR_STATUS = AppsmithError.INTERNAL_SERVER_ERROR.getHttpErrorCode(); + public static final String INTERNAL_SERVER_ERROR_CODE = AppsmithError.INTERNAL_SERVER_ERROR.getAppErrorCode(); + + private final SessionUserService sessionUserService; + private final UserService userService; + private final UserDataService userDataService; + private final TenantService tenantService; + private final ProductAlertService productAlertService; + private final NewPageService newPageService; + private final NewActionService newActionService; + private final ActionCollectionService actionCollectionService; + private final ThemeService themeService; + private final ApplicationPageService applicationPageService; + private final CustomJSLibService customJSLibService; + private final PluginService pluginService; + private final ApplicationService applicationService; + private final DatasourceService datasourceService; + private final MockDataService mockDataService; + + public ConsolidatedAPIServiceImpl( + SessionUserService sessionUserService, + UserService userService, + UserDataService userDataService, + TenantService tenantService, + ProductAlertService productAlertService, + NewPageService newPageService, + NewActionService newActionService, + ActionCollectionService actionCollectionService, + ThemeService themeService, + ApplicationPageService applicationPageService, + CustomJSLibService customJSLibService, + PluginService pluginService, + ApplicationService applicationService, + DatasourceService datasourceService, + MockDataService mockDataService) { + this.sessionUserService = sessionUserService; + this.userService = userService; + this.userDataService = userDataService; + this.tenantService = tenantService; + this.productAlertService = productAlertService; + this.newPageService = newPageService; + this.newActionService = newActionService; + this.actionCollectionService = actionCollectionService; + this.themeService = themeService; + this.applicationPageService = applicationPageService; + this.customJSLibService = customJSLibService; + this.pluginService = pluginService; + this.applicationService = applicationService; + this.datasourceService = datasourceService; + this.mockDataService = mockDataService; + } + + ResponseDTO getSuccessResponse(T data) { + return new ResponseDTO<>(HttpStatus.OK.value(), data, null); + } + + Mono> getErrorResponseMono(Throwable error, Class type) { + if (error instanceof AppsmithException appsmithException) { + return Mono.just(new ResponseDTO( + appsmithException.getHttpStatus(), + new ErrorDTO( + appsmithException.getAppErrorCode(), + appsmithException.getErrorType(), + appsmithException.getMessage(), + appsmithException.getTitle()))); + } + + return Mono.just(new ResponseDTO( + INTERNAL_SERVER_ERROR_STATUS, new ErrorDTO(INTERNAL_SERVER_ERROR_CODE, error.getMessage()))); + } + + /** + * This method is meant to be used by the client application at the time of 1st page load. Client currently makes + * several API calls to fetch all the required data. This method consolidates all that data and returns them as + * response hence enabling the client to fetch the required data via a single API call only. + * + * PLEASE TAKE CARE TO USE .cache() FOR Mono THAT GETS REUSED SO THAT FIRST PAGE LOAD PERFORMANCE DOES NOT DEGRADE. + */ + @Override + public Mono getConsolidatedInfoForPageLoad( + String defaultPageId, String applicationId, String branchName, ApplicationMode mode) { + + /* if either of pageId or applicationId are provided then application mode must also be provided */ + if (mode == null && (!isBlank(defaultPageId) || !isBlank(applicationId))) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, APP_MODE)); + } + + /* This object will serve as a container to hold the response of this method*/ + ConsolidatedAPIResponseDTO consolidatedAPIResponseDTO = new ConsolidatedAPIResponseDTO(); + + /* Get user profile data */ + Mono> userProfileDTOResponseDTOMono = sessionUserService + .getCurrentUser() + .flatMap(userService::buildUserProfileDTO) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, UserProfileDTO.class)); + + /* Get all feature flags data */ + Mono> featureFlagsForCurrentUserResponseDTOMonoCache = userDataService + .getFeatureFlagsForCurrentUser() + .map(res -> (Map) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, Map.class)) + .cache(); + + /* Get tenant config data */ + Mono> tenantResponseDTOMono = tenantService + .getTenantConfiguration() + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, Tenant.class)); + + /* Get any product alert info */ + Mono> productAlertResponseDTOMono = productAlertService + .getSingleApplicableMessage() + .map(messages -> { + if (!messages.isEmpty()) { + return messages.get(0); + } + + return new ProductAlertResponseDTO(); + }) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, ProductAlertResponseDTO.class)); + + if (isBlank(defaultPageId) && isBlank(applicationId)) { + + List> listOfCommonResponseMono = List.of( + userProfileDTOResponseDTOMono, + featureFlagsForCurrentUserResponseDTOMonoCache, + tenantResponseDTOMono, + productAlertResponseDTOMono); + + return Mono.zip(listOfCommonResponseMono, responseArray -> { + consolidatedAPIResponseDTO.setV1UsersMeResp((ResponseDTO) responseArray[0]); + consolidatedAPIResponseDTO.setV1UsersFeaturesResp((ResponseDTO>) responseArray[1]); + consolidatedAPIResponseDTO.setV1TenantsCurrentResp((ResponseDTO) responseArray[2]); + consolidatedAPIResponseDTO.setV1ProductAlertResp( + (ResponseDTO) responseArray[3]); + + return consolidatedAPIResponseDTO; + }); + } + + /* Get view mode - EDIT or PUBLISHED */ + boolean isViewMode = ApplicationMode.PUBLISHED.equals(mode); + + /* Fetch application id if not provided */ + Mono applicationIdMonoCache; + if (isBlank(applicationId)) { + applicationIdMonoCache = applicationPageService + .getPage(defaultPageId, isViewMode) + .map(PageDTO::getApplicationId) + .cache(); + } else { + applicationIdMonoCache = Mono.just(applicationId).cache(); + } + + /* Get all pages in application */ + Mono> applicationPagesDTOResponseDTOMonoCache = applicationIdMonoCache + .flatMap(appId -> newPageService.findApplicationPages(appId, null, branchName, mode)) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, ApplicationPagesDTO.class)) + .cache(); + + /* Get current theme */ + Mono> applicationThemeResponseDTOMono = applicationIdMonoCache + .flatMap(appId -> themeService.getApplicationTheme(appId, mode, branchName)) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, Theme.class)); + + /* Get all themes */ + Mono> ThemesListResponseDTOMono = applicationIdMonoCache + .flatMap(appId -> + themeService.getApplicationThemes(appId, branchName).collectList()) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* Get all custom JS libraries installed in the application */ + Mono> allJSLibsInContextDTOResponseDTOMono = applicationIdMonoCache + .flatMap(appId -> customJSLibService.getAllJSLibsInContext( + appId, CreatorContextType.APPLICATION, branchName, isViewMode)) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* Check if release_server_dsl_migrations_enabled flag is true for the user */ + Mono migrateDslMonoCache = featureFlagsForCurrentUserResponseDTOMonoCache + .map(ResponseDTO::getData) + .map(flagsMap -> { + if (!flagsMap.containsKey(FEATURE_FLAG_RELEASE_SERVER_DSL_MIGRATIONS_ENABLED)) { + return false; + } + + return (Boolean) flagsMap.get(FEATURE_FLAG_RELEASE_SERVER_DSL_MIGRATIONS_ENABLED); + }) + .cache(); + + Mono> currentPageDTOResponseDTOMono = Mono.empty(); + if (!isBlank(defaultPageId)) { + /* Get current page */ + currentPageDTOResponseDTOMono = migrateDslMonoCache + .flatMap(migrateDsl -> applicationPageService.getPageAndMigrateDslByBranchAndDefaultPageId( + defaultPageId, branchName, isViewMode, migrateDsl)) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, PageDTO.class)); + } + + /* Fetch view specific data */ + if (isViewMode) { + /* Get list of all actions in view mode */ + Mono> listOfActionViewResponseDTOMono = applicationIdMonoCache + .flatMap(appId -> newActionService + .getActionsForViewMode(appId, branchName) + .collectList()) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* Get list of all action collections in view mode */ + Mono> listOfActionCollectionViewResponseDTOMono = applicationIdMonoCache + .flatMap(appId -> actionCollectionService + .getActionCollectionsForViewMode(appId, branchName) + .collectList()) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* This list contains the Mono objects corresponding to all the data points required for view mode. All + * the Mono objects in this list will be evaluated via Mono.zip operator. + */ + List> listOfMonoForPublishedApp = new ArrayList<>(List.of( + userProfileDTOResponseDTOMono, + tenantResponseDTOMono, + featureFlagsForCurrentUserResponseDTOMonoCache, + applicationPagesDTOResponseDTOMonoCache, + applicationThemeResponseDTOMono, + ThemesListResponseDTOMono, + listOfActionViewResponseDTOMono, + listOfActionCollectionViewResponseDTOMono, + allJSLibsInContextDTOResponseDTOMono, + productAlertResponseDTOMono)); + + if (!isBlank(defaultPageId)) { + listOfMonoForPublishedApp.add(currentPageDTOResponseDTOMono); + } + + return Mono.zip(listOfMonoForPublishedApp, responseArray -> { + consolidatedAPIResponseDTO.setV1UsersMeResp((ResponseDTO) responseArray[0]); + consolidatedAPIResponseDTO.setV1TenantsCurrentResp((ResponseDTO) responseArray[1]); + consolidatedAPIResponseDTO.setV1UsersFeaturesResp((ResponseDTO>) responseArray[2]); + consolidatedAPIResponseDTO.setV1PagesResp((ResponseDTO) responseArray[3]); + consolidatedAPIResponseDTO.setV1ThemesApplicationCurrentModeResp((ResponseDTO) responseArray[4]); + consolidatedAPIResponseDTO.setV1ThemesResp((ResponseDTO>) responseArray[5]); + consolidatedAPIResponseDTO.setV1ActionsViewResp((ResponseDTO>) responseArray[6]); + consolidatedAPIResponseDTO.setV1CollectionsActionsViewResp( + (ResponseDTO>) responseArray[7]); + consolidatedAPIResponseDTO.setV1LibrariesApplicationResp( + (ResponseDTO>) responseArray[8]); + consolidatedAPIResponseDTO.setV1ProductAlertResp( + (ResponseDTO) responseArray[9]); + + if (!isBlank(defaultPageId)) { + consolidatedAPIResponseDTO.setV1PublishedPageResp((ResponseDTO) responseArray[10]); + } + + return consolidatedAPIResponseDTO; + }); + } else { + /* Get all actions in edit mode */ + Mono> listOfActionResponseDTOMono = applicationIdMonoCache + .flatMap(appId -> { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add(APPLICATION_ID, appId); + return newActionService + .getUnpublishedActions(params, branchName, false) + .collectList(); + }) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* Get all action collections in edit mode */ + Mono> listOfActionCollectionResponseDTOMono = applicationIdMonoCache.flatMap(appId -> { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add(APPLICATION_ID, appId); + return actionCollectionService + .getPopulatedActionCollectionsByViewMode(params, false, branchName) + .collectList() + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + }); + + /* Get all pages in edit mode post apply migrate DSL changes */ + Mono> listOfAllPageResponseDTOMono = migrateDslMonoCache + .flatMap(migrateDsl -> applicationPagesDTOResponseDTOMonoCache + .map(ResponseDTO::getData) + .map(ApplicationPagesDTO::getPages) + .flatMapMany(Flux::fromIterable) + .flatMap(page -> applicationPageService.getPageAndMigrateDslByBranchAndDefaultPageId( + page.getDefaultPageId(), branchName, false, migrateDsl)) + .collect(Collectors.toList())) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* Get all workspace id */ + Mono workspaceIdMonoCache = applicationPagesDTOResponseDTOMonoCache + .map(ResponseDTO::getData) + .map(ApplicationPagesDTO::getWorkspaceId) + .cache(); + + /* Get all plugins in workspace */ + Mono> listOfPluginsResponseDTOMonoCache = workspaceIdMonoCache + .flatMap(workspaceId -> { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add(WORKSPACE_ID, workspaceId); + return pluginService.get(params).collectList(); + }) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)) + .cache(); + + /* Get all datasources in workspace */ + Mono> listOfDatasourcesResponseDTOMonoCache = workspaceIdMonoCache + .flatMap(workspaceId -> { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add(WORKSPACE_ID, workspaceId); + return datasourceService.getAllWithStorages(params).collectList(); + }) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)) + .cache(); + + /* Get form config for all relevant plugins by following this rule: + * (a) there is at least one datasource of the plugin type alive in the workspace + * (b) include REST API and GraphQL API plugin always + * (c) ignore any other plugin + * */ + Mono> listOfFormConfigsResponseDTOMono = Mono.zip( + listOfPluginsResponseDTOMonoCache, listOfDatasourcesResponseDTOMonoCache) + .map(tuple2 -> { + Set setOfAllPluginIdsToGetFormConfig = new HashSet<>(); + List pluginList = tuple2.getT1().getData(); + List datasourcesList = tuple2.getT2().getData(); + + datasourcesList.stream() + .filter(datasource -> !isBlank(datasource.getPluginId())) + .forEach(datasource -> setOfAllPluginIdsToGetFormConfig.add(datasource.getPluginId())); + + pluginList.stream() + .filter(plugin -> REST_API_PLUGIN.equals(plugin.getPackageName()) + || GRAPHQL_PLUGIN.equals(plugin.getPackageName())) + .forEach(plugin -> setOfAllPluginIdsToGetFormConfig.add(plugin.getId())); + + return setOfAllPluginIdsToGetFormConfig; + }) + .flatMapMany(Flux::fromIterable) + .flatMap(pluginId -> + pluginService.getFormConfig(pluginId).map(formConfig -> Pair.of(pluginId, formConfig))) + .collectList() + .map(listOfFormConfig -> { + Map pluginIdToFormConfigMap = new HashMap<>(); + listOfFormConfig.stream().forEach(individualConfigMap -> { + String pluginId = individualConfigMap.getFirst(); + Map config = individualConfigMap.getSecond(); + pluginIdToFormConfigMap.put(pluginId, config); + }); + + return pluginIdToFormConfigMap; + }) + .map(res -> (Map) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, Map.class)); + + /* List of mock datasources available to the user */ + Mono> mockDataListResponseDTOMono = mockDataService + .getMockDataSet() + .map(MockDataDTO::getMockdbs) + .map(res -> (List) res) + .map(this::getSuccessResponse) + .onErrorResume(error -> getErrorResponseMono(error, List.class)); + + /* This list contains the Mono objects corresponding to all the data points required for edit mode. All + * the Mono objects in this list will be evaluated via Mono.zip operator + */ + List> listOfMonoForEditMode = new ArrayList<>(List.of( + userProfileDTOResponseDTOMono, + tenantResponseDTOMono, + featureFlagsForCurrentUserResponseDTOMonoCache, + applicationPagesDTOResponseDTOMonoCache, + applicationThemeResponseDTOMono, + ThemesListResponseDTOMono, + allJSLibsInContextDTOResponseDTOMono, + productAlertResponseDTOMono, + listOfActionResponseDTOMono, + listOfActionCollectionResponseDTOMono, + listOfAllPageResponseDTOMono, + listOfPluginsResponseDTOMonoCache, + listOfDatasourcesResponseDTOMonoCache, + listOfFormConfigsResponseDTOMono, + mockDataListResponseDTOMono)); + + if (!isBlank(defaultPageId)) { + listOfMonoForEditMode.add(currentPageDTOResponseDTOMono); + } + + return Mono.zip(listOfMonoForEditMode, responseArray -> { + consolidatedAPIResponseDTO.setV1UsersMeResp((ResponseDTO) responseArray[0]); + consolidatedAPIResponseDTO.setV1TenantsCurrentResp((ResponseDTO) responseArray[1]); + consolidatedAPIResponseDTO.setV1UsersFeaturesResp((ResponseDTO>) responseArray[2]); + consolidatedAPIResponseDTO.setV1PagesResp((ResponseDTO) responseArray[3]); + consolidatedAPIResponseDTO.setV1ThemesApplicationCurrentModeResp((ResponseDTO) responseArray[4]); + consolidatedAPIResponseDTO.setV1ThemesResp((ResponseDTO>) responseArray[5]); + consolidatedAPIResponseDTO.setV1LibrariesApplicationResp( + (ResponseDTO>) responseArray[6]); + consolidatedAPIResponseDTO.setV1ProductAlertResp( + (ResponseDTO) responseArray[7]); + consolidatedAPIResponseDTO.setV1ActionsResp((ResponseDTO>) responseArray[8]); + consolidatedAPIResponseDTO.setV1CollectionsActionsResp( + (ResponseDTO>) responseArray[9]); + consolidatedAPIResponseDTO.setV1PageDSLs((ResponseDTO>) responseArray[10]); + consolidatedAPIResponseDTO.setV1PluginsResp((ResponseDTO>) responseArray[11]); + consolidatedAPIResponseDTO.setV1DatasourcesResp((ResponseDTO>) responseArray[12]); + consolidatedAPIResponseDTO.setV1PluginFormConfigsResp( + (ResponseDTO>) responseArray[13]); + consolidatedAPIResponseDTO.setV1DatasourcesMockResp((ResponseDTO>) responseArray[14]); + + if (!isBlank(defaultPageId)) { + consolidatedAPIResponseDTO.setV1PageResp((ResponseDTO) responseArray[15]); + } + + return consolidatedAPIResponseDTO; + }); + } + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java index 095cb3cd94..3fa73f2f2a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java @@ -163,7 +163,7 @@ public class DatasourceContextServiceCEImpl implements DatasourceContextServiceC public boolean shouldCacheContextForThisPlugin(Plugin plugin) { // !(a || b) => (!a) & (!b) return !PluginConstants.PackageName.REST_API_PLUGIN.equals(plugin.getPackageName()) - && !PluginConstants.PackageName.GRAPH_QL_PLUGIN.equals(plugin.getPackageName()); + && !PluginConstants.PackageName.GRAPHQL_PLUGIN.equals(plugin.getPackageName()); } public Mono updateDatasourceAndSetAuthentication(Object connection, DatasourceStorage datasourceStorage) { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ConsolidatedAPIServiceImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ConsolidatedAPIServiceImplTest.java new file mode 100644 index 0000000000..88e3e59499 --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ConsolidatedAPIServiceImplTest.java @@ -0,0 +1,614 @@ +package com.appsmith.server.services; + +import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.Datasource; +import com.appsmith.server.actioncollections.base.ActionCollectionService; +import com.appsmith.server.applications.base.ApplicationService; +import com.appsmith.server.datasources.base.DatasourceService; +import com.appsmith.server.domains.ApplicationMode; +import com.appsmith.server.domains.CustomJSLib; +import com.appsmith.server.domains.Plugin; +import com.appsmith.server.domains.Tenant; +import com.appsmith.server.domains.Theme; +import com.appsmith.server.domains.User; +import com.appsmith.server.dtos.ActionCollectionDTO; +import com.appsmith.server.dtos.ActionCollectionViewDTO; +import com.appsmith.server.dtos.ActionViewDTO; +import com.appsmith.server.dtos.ApplicationPagesDTO; +import com.appsmith.server.dtos.ConsolidatedAPIResponseDTO; +import com.appsmith.server.dtos.MockDataDTO; +import com.appsmith.server.dtos.MockDataSet; +import com.appsmith.server.dtos.PageDTO; +import com.appsmith.server.dtos.PageNameIdDTO; +import com.appsmith.server.dtos.ProductAlertResponseDTO; +import com.appsmith.server.dtos.UserProfileDTO; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.jslibs.base.CustomJSLibService; +import com.appsmith.server.newactions.base.NewActionService; +import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.plugins.base.PluginService; +import com.appsmith.server.themes.base.ThemeService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; + +@SpringBootTest +@ExtendWith(SpringExtension.class) +public class ConsolidatedAPIServiceImplTest { + + @SpyBean + ConsolidatedAPIService consolidatedAPIService; + + @SpyBean + SessionUserService sessionUserService; + + @SpyBean + UserService userService; + + @SpyBean + UserDataService userDataService; + + @SpyBean + TenantService tenantService; + + @SpyBean + ProductAlertService productAlertService; + + @SpyBean + NewPageService newPageService; + + @SpyBean + NewActionService newActionService; + + @SpyBean + ActionCollectionService actionCollectionService; + + @SpyBean + ThemeService themeService; + + @SpyBean + ApplicationPageService applicationPageService; + + @SpyBean + CustomJSLibService customJSLibService; + + @SpyBean + PluginService pluginService; + + @SpyBean + ApplicationService applicationService; + + @SpyBean + DatasourceService datasourceService; + + @SpyBean + MockDataService mockDataService; + + @Test + public void testErrorWhenModeIsNullAndPageIdAvailable() { + Mono consolidatedInfoForPageLoad = + consolidatedAPIService.getConsolidatedInfoForPageLoad("pageId", null, null, null); + StepVerifier.create(consolidatedInfoForPageLoad).verifyErrorSatisfies(error -> { + assertTrue(error instanceof AppsmithException); + assertEquals("Please enter a valid parameter appMode.", error.getMessage()); + }); + } + + @Test + public void testPageLoadResponseWhenPageIdAndApplicationIdMissing() { + User sampleUser = new User(); + doReturn(Mono.just(sampleUser)).when(sessionUserService).getCurrentUser(); + + UserProfileDTO sampleUserProfileDTO = new UserProfileDTO(); + sampleUserProfileDTO.setName("sampleUserProfileDTO"); + doReturn(Mono.just(sampleUserProfileDTO)).when(userService).buildUserProfileDTO(any()); + + Map sampleFeatureFlagMap = new HashMap<>(); + sampleFeatureFlagMap.put("sampleFeatureFlag", true); + doReturn(Mono.just(sampleFeatureFlagMap)).when(userDataService).getFeatureFlagsForCurrentUser(); + + Tenant sampleTenant = new Tenant(); + sampleTenant.setDisplayName("sampleTenant"); + doReturn(Mono.just(sampleTenant)).when(tenantService).getTenantConfiguration(); + + ProductAlertResponseDTO sampleProductAlertResponseDTO = new ProductAlertResponseDTO(); + sampleProductAlertResponseDTO.setTitle("sampleProductAlert"); + doReturn(Mono.just(List.of(sampleProductAlertResponseDTO))) + .when(productAlertService) + .getSingleApplicableMessage(); + + Mono consolidatedInfoForPageLoad = + consolidatedAPIService.getConsolidatedInfoForPageLoad( + "pageId", "appId", "branch", ApplicationMode.PUBLISHED); + StepVerifier.create(consolidatedInfoForPageLoad) + .assertNext(consolidatedAPIResponseDTO -> { + assertNotNull(consolidatedAPIResponseDTO.getV1UsersMeResp()); + assertEquals( + "sampleUserProfileDTO", + consolidatedAPIResponseDTO + .getV1UsersMeResp() + .getData() + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1TenantsCurrentResp()); + assertEquals( + "sampleTenant", + consolidatedAPIResponseDTO + .getV1TenantsCurrentResp() + .getData() + .getDisplayName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1UsersFeaturesResp()); + assertTrue(consolidatedAPIResponseDTO + .getV1UsersFeaturesResp() + .getData() + .get("sampleFeatureFlag")); + + assertNotNull(consolidatedAPIResponseDTO.getV1ProductAlertResp()); + assertEquals( + "sampleProductAlert", + consolidatedAPIResponseDTO + .getV1ProductAlertResp() + .getData() + .getTitle()); + }) + .verifyComplete(); + } + + @Test + public void testPageLoadResponseForViewMode() { + User sampleUser = new User(); + doReturn(Mono.just(sampleUser)).when(sessionUserService).getCurrentUser(); + + UserProfileDTO sampleUserProfileDTO = new UserProfileDTO(); + sampleUserProfileDTO.setName("sampleUserProfileDTO"); + doReturn(Mono.just(sampleUserProfileDTO)).when(userService).buildUserProfileDTO(any()); + + Map sampleFeatureFlagMap = new HashMap<>(); + sampleFeatureFlagMap.put("sampleFeatureFlag", true); + doReturn(Mono.just(sampleFeatureFlagMap)).when(userDataService).getFeatureFlagsForCurrentUser(); + + Tenant sampleTenant = new Tenant(); + sampleTenant.setDisplayName("sampleTenant"); + doReturn(Mono.just(sampleTenant)).when(tenantService).getTenantConfiguration(); + + ProductAlertResponseDTO sampleProductAlertResponseDTO = new ProductAlertResponseDTO(); + sampleProductAlertResponseDTO.setTitle("sampleProductAlert"); + doReturn(Mono.just(List.of(sampleProductAlertResponseDTO))) + .when(productAlertService) + .getSingleApplicableMessage(); + + ApplicationPagesDTO sampleApplicationPagesDTO = new ApplicationPagesDTO(); + sampleApplicationPagesDTO.setWorkspaceId("sampleWorkspaceId"); + doReturn(Mono.just(sampleApplicationPagesDTO)) + .when(newPageService) + .findApplicationPages(anyString(), any(), anyString(), any()); + + Theme sampleTheme = new Theme(); + sampleTheme.setName("sampleTheme"); + doReturn(Mono.just(sampleTheme)).when(themeService).getApplicationTheme(anyString(), any(), anyString()); + doReturn(Flux.just(sampleTheme)).when(themeService).getApplicationThemes(anyString(), anyString()); + + CustomJSLib sampleCustomJSLib = new CustomJSLib(); + sampleCustomJSLib.setName("sampleJSLib"); + doReturn(Mono.just(List.of(sampleCustomJSLib))) + .when(customJSLibService) + .getAllJSLibsInContext(anyString(), any(), anyString(), anyBoolean()); + + PageDTO samplePageDTO = new PageDTO(); + samplePageDTO.setName("samplePageDTO"); + doReturn(Mono.just(samplePageDTO)) + .when(applicationPageService) + .getPageAndMigrateDslByBranchAndDefaultPageId(anyString(), anyString(), anyBoolean(), anyBoolean()); + + ActionViewDTO sampleActionViewDTO = new ActionViewDTO(); + sampleActionViewDTO.setName("sampleActionViewDTO"); + doReturn(Flux.just(sampleActionViewDTO)).when(newActionService).getActionsForViewMode(anyString(), anyString()); + + ActionCollectionViewDTO sampleActionCollectionViewDTO = new ActionCollectionViewDTO(); + sampleActionCollectionViewDTO.setName("sampleActionCollectionViewDTO"); + doReturn(Flux.just(sampleActionCollectionViewDTO)) + .when(actionCollectionService) + .getActionCollectionsForViewMode(anyString(), anyString()); + + Mono consolidatedInfoForPageLoad = + consolidatedAPIService.getConsolidatedInfoForPageLoad( + "pageId", "appId", "branch", ApplicationMode.PUBLISHED); + StepVerifier.create(consolidatedInfoForPageLoad) + .assertNext(consolidatedAPIResponseDTO -> { + assertNotNull(consolidatedAPIResponseDTO.getV1ActionsViewResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1ActionsViewResp() + .getData() + .size()); + assertEquals( + "sampleActionViewDTO", + consolidatedAPIResponseDTO + .getV1ActionsViewResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1UsersMeResp()); + assertEquals( + "sampleUserProfileDTO", + consolidatedAPIResponseDTO + .getV1UsersMeResp() + .getData() + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1TenantsCurrentResp()); + assertEquals( + "sampleTenant", + consolidatedAPIResponseDTO + .getV1TenantsCurrentResp() + .getData() + .getDisplayName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1UsersFeaturesResp()); + assertTrue(consolidatedAPIResponseDTO + .getV1UsersFeaturesResp() + .getData() + .get("sampleFeatureFlag")); + + assertNotNull(consolidatedAPIResponseDTO.getV1PagesResp()); + assertEquals( + "sampleWorkspaceId", + consolidatedAPIResponseDTO + .getV1PagesResp() + .getData() + .getWorkspaceId()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ThemesApplicationCurrentModeResp()); + assertEquals( + "sampleTheme", + consolidatedAPIResponseDTO + .getV1ThemesApplicationCurrentModeResp() + .getData() + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ThemesResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1ThemesResp() + .getData() + .size()); + assertEquals( + "sampleTheme", + consolidatedAPIResponseDTO + .getV1ThemesResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1CollectionsActionsViewResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1CollectionsActionsViewResp() + .getData() + .size()); + assertEquals( + "sampleActionCollectionViewDTO", + consolidatedAPIResponseDTO + .getV1CollectionsActionsViewResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1PublishedPageResp()); + assertEquals( + "samplePageDTO", + consolidatedAPIResponseDTO + .getV1PublishedPageResp() + .getData() + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1LibrariesApplicationResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1LibrariesApplicationResp() + .getData() + .size()); + assertEquals( + "sampleJSLib", + consolidatedAPIResponseDTO + .getV1LibrariesApplicationResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ProductAlertResp()); + assertEquals( + "sampleProductAlert", + consolidatedAPIResponseDTO + .getV1ProductAlertResp() + .getData() + .getTitle()); + }) + .verifyComplete(); + } + + @Test + public void testPageLoadResponseForEditMode() { + User sampleUser = new User(); + doReturn(Mono.just(sampleUser)).when(sessionUserService).getCurrentUser(); + + UserProfileDTO sampleUserProfileDTO = new UserProfileDTO(); + sampleUserProfileDTO.setName("sampleUserProfileDTO"); + doReturn(Mono.just(sampleUserProfileDTO)).when(userService).buildUserProfileDTO(any()); + + Map sampleFeatureFlagMap = new HashMap<>(); + sampleFeatureFlagMap.put("sampleFeatureFlag", true); + doReturn(Mono.just(sampleFeatureFlagMap)).when(userDataService).getFeatureFlagsForCurrentUser(); + + Tenant sampleTenant = new Tenant(); + sampleTenant.setDisplayName("sampleTenant"); + doReturn(Mono.just(sampleTenant)).when(tenantService).getTenantConfiguration(); + + ProductAlertResponseDTO sampleProductAlertResponseDTO = new ProductAlertResponseDTO(); + sampleProductAlertResponseDTO.setTitle("sampleProductAlert"); + doReturn(Mono.just(List.of(sampleProductAlertResponseDTO))) + .when(productAlertService) + .getSingleApplicableMessage(); + + ApplicationPagesDTO sampleApplicationPagesDTO = new ApplicationPagesDTO(); + sampleApplicationPagesDTO.setWorkspaceId("sampleWorkspaceId"); + doReturn(Mono.just(sampleApplicationPagesDTO)) + .when(newPageService) + .findApplicationPages(anyString(), any(), anyString(), any()); + + Theme sampleTheme = new Theme(); + sampleTheme.setName("sampleTheme"); + doReturn(Mono.just(sampleTheme)).when(themeService).getApplicationTheme(anyString(), any(), anyString()); + doReturn(Flux.just(sampleTheme)).when(themeService).getApplicationThemes(anyString(), anyString()); + + CustomJSLib sampleCustomJSLib = new CustomJSLib(); + sampleCustomJSLib.setName("sampleJSLib"); + doReturn(Mono.just(List.of(sampleCustomJSLib))) + .when(customJSLibService) + .getAllJSLibsInContext(anyString(), any(), anyString(), anyBoolean()); + + PageDTO samplePageDTO = new PageDTO(); + samplePageDTO.setName("samplePageDTO"); + doReturn(Mono.just(samplePageDTO)) + .when(applicationPageService) + .getPageAndMigrateDslByBranchAndDefaultPageId(anyString(), anyString(), anyBoolean(), anyBoolean()); + doReturn(Mono.just(samplePageDTO)) + .when(applicationPageService) + .getPageAndMigrateDslByBranchAndDefaultPageId(anyString(), anyString(), anyBoolean(), anyBoolean()); + + ActionDTO sampleActionDTO = new ActionDTO(); + sampleActionDTO.setName("sampleActionDTO"); + doReturn(Flux.just(sampleActionDTO)) + .when(newActionService) + .getUnpublishedActions(any(), anyString(), anyBoolean()); + + ActionCollectionDTO sampleActionCollectionDTO = new ActionCollectionDTO(); + sampleActionCollectionDTO.setName("sampleActionCollectionDTO"); + doReturn(Flux.just(sampleActionCollectionDTO)) + .when(actionCollectionService) + .getPopulatedActionCollectionsByViewMode(any(), anyBoolean(), anyString()); + + PageNameIdDTO samplePageNameIdDTO = new PageNameIdDTO(); + samplePageNameIdDTO.setName("samplePageNameIdDTO"); + samplePageNameIdDTO.setDefaultPageId("pageId"); + sampleApplicationPagesDTO.setPages(List.of(samplePageNameIdDTO)); + + Plugin samplePlugin = new Plugin(); + samplePlugin.setName("samplePlugin"); + doReturn(Flux.just(samplePlugin)).when(pluginService).get(any()); + + Datasource sampleDatasource = new Datasource(); + sampleDatasource.setName("sampleDatasource"); + sampleDatasource.setPluginId("samplePluginId"); + doReturn(Flux.just(sampleDatasource)).when(datasourceService).getAllWithStorages(any()); + + Map sampleFormConfig = new HashMap<>(); + sampleFormConfig.put("key", Map.of()); + doReturn(Mono.just(sampleFormConfig)).when(pluginService).getFormConfig(anyString()); + + MockDataSet sampleMockDataSet = new MockDataSet(); + sampleMockDataSet.setName("sampleMockDataSet"); + MockDataDTO sampleMockDataDTO = new MockDataDTO(); + sampleMockDataDTO.setMockdbs(List.of(sampleMockDataSet)); + doReturn(Mono.just(sampleMockDataDTO)).when(mockDataService).getMockDataSet(); + + Mono consolidatedInfoForPageLoad = + consolidatedAPIService.getConsolidatedInfoForPageLoad( + "pageId", "appId", "branch", ApplicationMode.EDIT); + StepVerifier.create(consolidatedInfoForPageLoad) + .assertNext(consolidatedAPIResponseDTO -> { + assertNotNull(consolidatedAPIResponseDTO.getV1UsersMeResp()); + assertEquals( + "sampleUserProfileDTO", + consolidatedAPIResponseDTO + .getV1UsersMeResp() + .getData() + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1TenantsCurrentResp()); + assertEquals( + "sampleTenant", + consolidatedAPIResponseDTO + .getV1TenantsCurrentResp() + .getData() + .getDisplayName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1UsersFeaturesResp()); + assertTrue(consolidatedAPIResponseDTO + .getV1UsersFeaturesResp() + .getData() + .get("sampleFeatureFlag")); + + assertNotNull(consolidatedAPIResponseDTO.getV1PagesResp()); + assertEquals( + "sampleWorkspaceId", + consolidatedAPIResponseDTO + .getV1PagesResp() + .getData() + .getWorkspaceId()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ThemesApplicationCurrentModeResp()); + assertEquals( + "sampleTheme", + consolidatedAPIResponseDTO + .getV1ThemesApplicationCurrentModeResp() + .getData() + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ThemesResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1ThemesResp() + .getData() + .size()); + assertEquals( + "sampleTheme", + consolidatedAPIResponseDTO + .getV1ThemesResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1PagesResp()); + assertEquals( + "sampleWorkspaceId", + consolidatedAPIResponseDTO + .getV1PagesResp() + .getData() + .getWorkspaceId()); + + assertNotNull(consolidatedAPIResponseDTO.getV1LibrariesApplicationResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1LibrariesApplicationResp() + .getData() + .size()); + assertEquals( + "sampleJSLib", + consolidatedAPIResponseDTO + .getV1LibrariesApplicationResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ProductAlertResp()); + assertEquals( + "sampleProductAlert", + consolidatedAPIResponseDTO + .getV1ProductAlertResp() + .getData() + .getTitle()); + + assertNotNull(consolidatedAPIResponseDTO.getV1ActionsResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1ActionsResp() + .getData() + .size()); + assertEquals( + "sampleActionDTO", + consolidatedAPIResponseDTO + .getV1ActionsResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1CollectionsActionsResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1CollectionsActionsResp() + .getData() + .size()); + assertEquals( + "sampleActionCollectionDTO", + consolidatedAPIResponseDTO + .getV1CollectionsActionsResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1PageDSLs()); + assertEquals( + 1, + consolidatedAPIResponseDTO.getV1PageDSLs().getData().size()); + assertEquals( + "samplePageDTO", + consolidatedAPIResponseDTO + .getV1PageDSLs() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1PluginsResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1PluginsResp() + .getData() + .size()); + assertEquals( + "samplePlugin", + consolidatedAPIResponseDTO + .getV1PluginsResp() + .getData() + .get(0) + .getName()); + + assertNotNull(consolidatedAPIResponseDTO.getV1PluginFormConfigsResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1PluginFormConfigsResp() + .getData() + .keySet() + .size()); + assertTrue(consolidatedAPIResponseDTO + .getV1PluginFormConfigsResp() + .getData() + .containsKey("samplePluginId")); + + assertNotNull(consolidatedAPIResponseDTO.getV1DatasourcesMockResp()); + assertEquals( + 1, + consolidatedAPIResponseDTO + .getV1DatasourcesMockResp() + .getData() + .size()); + assertEquals( + "sampleMockDataSet", + consolidatedAPIResponseDTO + .getV1DatasourcesMockResp() + .getData() + .get(0) + .getName()); + }) + .verifyComplete(); + } +}