diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCE.java index 82ea789ecf99..6906857d3d48 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCE.java @@ -2,6 +2,7 @@ import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.GitAuth; import com.appsmith.server.dtos.ApplicationAccessDTO; import com.appsmith.server.dtos.GitAuthDTO; @@ -95,4 +96,7 @@ Mono setAppTheme( Mono isApplicationConnectedToGit(String applicationId); Mono updateProtectedBranches(String applicationId, List protectedBranches); + + Mono findByDefaultIdBranchNameAndApplicationMode( + String defaultApplicationId, String branchName, ApplicationMode mode); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java index 1f5f74f27404..068be68114dd 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java @@ -659,24 +659,15 @@ private Flux setTransientFields(Flux applicationsFlux) permissionGroupService.getPublicPermissionGroupId().cache().repeat(); // Set isPublic field if the application is public - Flux updatedApplicationWithIsPublicFlux = permissionGroupService - .getPublicPermissionGroupId() - .cache() - .repeat() - .zipWith(applicationsFlux) - .map(tuple -> { - Application application = tuple.getT2(); - String publicPermissionGroupId = tuple.getT1(); - - application.setIsPublic(permissionGroupService.isEntityAccessible( - application, - applicationPermission.getReadPermission().getValue(), - publicPermissionGroupId)); + return publicPermissionGroupIdFlux.zipWith(applicationsFlux).map(tuple -> { + Application application = tuple.getT2(); + String publicPermissionGroupId = tuple.getT1(); - return application; - }); + application.setIsPublic(permissionGroupService.isEntityAccessible( + application, applicationPermission.getReadPermission().getValue(), publicPermissionGroupId)); - return updatedApplicationWithIsPublicFlux; + return application; + }); } /** @@ -884,7 +875,7 @@ public Mono findBranchedApplicationId( AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, defaultApplicationId + ", " + branchName))) - .map(Application::getId); + .map(application -> application.getId()); } public Mono findBranchedApplicationId( @@ -1050,4 +1041,23 @@ public Mono updateProtectedBranches(String applicationId, List pro })) .then(); } + + /** + * Gets branched application with the right permission set based on mode of application + * @param defaultApplicationId : default app id + * @param branchName : branch name of the application + * @param mode : is it edit mode or view mode + * @return : returns a publisher of branched application + */ + @Override + public Mono findByDefaultIdBranchNameAndApplicationMode( + String defaultApplicationId, String branchName, ApplicationMode mode) { + AclPermission permissionForApplication = ApplicationMode.PUBLISHED.equals(mode) + ? applicationPermission.getReadPermission() + : applicationPermission.getEditPermission(); + + return findByBranchNameAndDefaultApplicationId(branchName, defaultApplicationId, permissionForApplication) + // sets isPublic field in the application + .flatMap(this::setTransientFields); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCE.java index 8aa463b38a75..4ff02f8620a5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCE.java @@ -1,6 +1,7 @@ package com.appsmith.server.newpages.base; import com.appsmith.server.acl.AclPermission; +import com.appsmith.server.domains.Application; import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.NewPage; @@ -92,4 +93,9 @@ Mono findByGitSyncIdAndDefaultApplicationId( Flux findPageSlugsByApplicationIds(List applicationIds, AclPermission aclPermission); Mono publishPages(Collection pageIds, AclPermission permission); + + ApplicationPagesDTO getApplicationPagesDTO(Application application, List newPages, boolean viewMode); + + Mono createApplicationPagesDTO( + Application branchedApplication, List newPages, boolean viewMode, boolean isRecentlyAccessed); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCEImpl.java index d07923171050..fc3bd101e386 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/base/NewPageServiceCEImpl.java @@ -30,6 +30,7 @@ import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; import org.bson.types.ObjectId; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -38,7 +39,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -222,30 +222,31 @@ public Mono deleteAll() { public Mono findApplicationPagesByApplicationIdViewMode( String applicationId, Boolean view, boolean markApplicationAsRecentlyAccessed) { - AclPermission permission; - if (view) { - permission = applicationPermission.getReadPermission(); - } else { - permission = applicationPermission.getEditPermission(); - } + AclPermission permission = Boolean.TRUE.equals(view) + ? applicationPermission.getReadPermission() + : applicationPermission.getEditPermission(); Mono applicationMono = applicationService .findById(applicationId, permission) .switchIfEmpty(Mono.error(new AppsmithException( AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId))) - // Throw a 404 error if the application has never been published .flatMap(application -> { - if (Boolean.TRUE.equals(view)) { - if (application.getPublishedPages() == null - || application.getPublishedPages().isEmpty()) { - // We are trying to fetch published pages but they don't exist because the application - // hasn't been published yet - return Mono.error(new AppsmithException( - AppsmithError.ACL_NO_RESOURCE_FOUND, - FieldName.PUBLISHED_APPLICATION, - application.getId())); - } + if (!Boolean.TRUE.equals(view)) { + return Mono.just(application); } + + if (application.getPublishedPages() == null + || application.getPublishedPages().isEmpty()) { + // We are trying to fetch published pages but they don't exist because the application + // hasn't been published yet + + // Throw a 404 error if the application has never been published + return Mono.error(new AppsmithException( + AppsmithError.ACL_NO_RESOURCE_FOUND, + FieldName.PUBLISHED_APPLICATION, + application.getId())); + } + return Mono.just(application); }) .flatMap(application -> { @@ -264,121 +265,155 @@ public Mono findApplicationPagesByApplicationIdViewMode( }) .cache(); - Mono defaultPageIdMono = applicationMono.map(application -> { - String defaultPageId = null; - List applicationPages; - if (Boolean.TRUE.equals(view)) { - applicationPages = application.getPublishedPages(); - } else { - applicationPages = application.getPages(); - } - - for (ApplicationPage applicationPage : applicationPages) { - if (Boolean.TRUE.equals(applicationPage.getIsDefault())) { - defaultPageId = applicationPage.getId(); - } - } - if (!StringUtils.hasLength(defaultPageId) && !CollectionUtils.isEmpty(applicationPages)) { - log.error("application {} has no default page, returning first page as default", application.getId()); - defaultPageId = applicationPages.get(0).getId(); - } - return defaultPageId; - }); - - Mono> pagesListMono = applicationMono + return applicationMono .map(application -> { - List pages; - if (Boolean.TRUE.equals(view)) { - pages = application.getPublishedPages(); - } else { - pages = application.getPages(); - } + List pages = getApplicationPages(application, view); return pages.stream().map(page -> page.getId()).collect(Collectors.toList()); }) .flatMapMany(pageIds -> repository.findAllPageDTOsByIds(pageIds, pagePermission.getReadPermission())) .collectList() - .flatMap(pagesFromDb -> Mono.zip(Mono.just(pagesFromDb), defaultPageIdMono, applicationMono)) - .flatMap(tuple -> { + .zipWith(applicationMono) + .map(tuple -> { log.debug("Retrieved Page DTOs from DB ..."); List pagesFromDb = tuple.getT1(); - String defaultPageId = tuple.getT2(); + Application application = tuple.getT2(); + return getApplicationPagesDTO(application, pagesFromDb, view); + }); + } - List pageNameIdDTOList = new ArrayList<>(); - List pages = tuple.getT3().getPages(); - List publishedPages = tuple.getT3().getPublishedPages(); - Map pagesOrder = new HashMap<>(); - Map publishedPagesOrder = new HashMap<>(); + /** + * Creates applicationPagesDTO and then updates the resources with default resources + * + * @param branchedApplication : branched application + * @param newPages : list of pages for the given mode + * @param viewMode : is application in viewMode + * @param markRecentlyAccessed : is + * @return : returns the getApplicationPagesDTO + */ + @Override + public Mono createApplicationPagesDTO( + Application branchedApplication, List newPages, boolean viewMode, boolean markRecentlyAccessed) { - if (Boolean.TRUE.equals(view)) { - for (int i = 0; i < publishedPages.size(); i++) { - publishedPagesOrder.put(publishedPages.get(i).getId(), i); - } - } else { - for (int i = 0; i < pages.size(); i++) { - pagesOrder.put(pages.get(i).getId(), i); - } - } + Mono markedRecentlyAccessedMono = Mono.empty(); - for (NewPage pageFromDb : pagesFromDb) { + if (Boolean.TRUE.equals(markRecentlyAccessed)) { + markedRecentlyAccessedMono = userDataService + .updateLastUsedResourceAndWorkspaceList( + branchedApplication.getId(), + branchedApplication.getWorkspaceId(), + WorkspaceResourceContext.APPLICATIONS) + .then(); + } - PageNameIdDTO pageNameIdDTO = new PageNameIdDTO(); + return markedRecentlyAccessedMono + .then(Mono.fromCallable(() -> getApplicationPagesDTO(branchedApplication, newPages, viewMode))) + .map(responseUtils::updateApplicationPagesDTOWithDefaultResources); + } - pageNameIdDTO.setId(pageFromDb.getId()); + private List getApplicationPages(Application application, boolean viewMode) { + return Boolean.TRUE.equals(viewMode) ? application.getPublishedPages() : application.getPages(); + } - if (pageFromDb.getDefaultResources() == null) { - return Mono.error(new AppsmithException( - AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "page", pageFromDb.getId())); - } - pageNameIdDTO.setDefaultPageId( - pageFromDb.getDefaultResources().getPageId()); - PageDTO pageDTO; - if (Boolean.TRUE.equals(view)) { - if (pageFromDb.getPublishedPage() == null) { - // We are trying to fetch published page but it doesnt exist because the page hasn't - // been published yet - return Mono.error(new AppsmithException( - AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.PAGE, pageFromDb.getId())); - } - pageDTO = pageFromDb.getPublishedPage(); - } else { - pageDTO = pageFromDb.getUnpublishedPage(); - } - pageNameIdDTO.setName(pageDTO.getName()); - pageNameIdDTO.setIsHidden(pageDTO.getIsHidden()); - pageNameIdDTO.setSlug(pageDTO.getSlug()); - pageNameIdDTO.setIcon(pageDTO.getIcon()); - pageNameIdDTO.setCustomSlug(pageDTO.getCustomSlug()); - pageNameIdDTO.setUserPermissions(pageFromDb.getUserPermissions()); - - if (pageNameIdDTO.getId().equals(defaultPageId)) { - pageNameIdDTO.setIsDefault(true); - } else { - pageNameIdDTO.setIsDefault(false); - } - pageNameIdDTOList.add(pageNameIdDTO); - } - if (Boolean.TRUE.equals(view)) { - Collections.sort( - pageNameIdDTOList, Comparator.comparing(item -> publishedPagesOrder.get(item.getId()))); - } else { - Collections.sort(pageNameIdDTOList, Comparator.comparing(item -> pagesOrder.get(item.getId()))); - } - return Mono.just(pageNameIdDTOList); - }); + private String getHomePageId(Application application, boolean viewMode) { + String homePageId = null; + List applicationPages = getApplicationPages(application, viewMode); + + for (ApplicationPage applicationPage : applicationPages) { + if (Boolean.TRUE.equals(applicationPage.getIsDefault())) { + homePageId = applicationPage.getId(); + break; + } + } + + if (!StringUtils.hasLength(homePageId) && !CollectionUtils.isEmpty(applicationPages)) { + log.error("application {} has no default page, returning first page as default", application.getId()); + homePageId = applicationPages.get(0).getId(); + } + + return homePageId; + } + + /** + * Creates ApplicationPagesDTO + * @param application : branched application + * @param newPages : list of pages for the given mode + * @param viewMode : is application in viewMode + * @return : returns the getApplicationPagesDTO + */ + public ApplicationPagesDTO getApplicationPagesDTO( + Application application, List newPages, boolean viewMode) { + + String homePageId = getHomePageId(application, viewMode); + List pageNameIdDTOList = new ArrayList<>(); + List applicationPages = application.getPages(); + List publishedApplicationPages = application.getPublishedPages(); + + Map pagesOrder = new HashMap<>(); + Map publishedPagesOrder = new HashMap<>(); + + if (Boolean.TRUE.equals(viewMode)) { + for (int i = 0; i < publishedApplicationPages.size(); i++) { + publishedPagesOrder.put(publishedApplicationPages.get(i).getId(), i); + } + + } else { + for (int i = 0; i < applicationPages.size(); i++) { + pagesOrder.put(applicationPages.get(i).getId(), i); + } + } + + for (NewPage pageFromDb : newPages) { + PageNameIdDTO pageNameIdDTO = getPageNameIdDTO(pageFromDb, homePageId, viewMode); + pageNameIdDTOList.add(pageNameIdDTO); + } + + if (Boolean.TRUE.equals(viewMode)) { + pageNameIdDTOList.sort(Comparator.comparing(item -> publishedPagesOrder.get(item.getId()))); + } else { + pageNameIdDTOList.sort(Comparator.comparing(item -> pagesOrder.get(item.getId()))); + } + + application.setPages(null); + application.setPublishedPages(null); + application.setViewMode(viewMode); + + ApplicationPagesDTO applicationPagesDTO = new ApplicationPagesDTO(); + applicationPagesDTO.setWorkspaceId(application.getWorkspaceId()); + applicationPagesDTO.setPages(pageNameIdDTOList); + applicationPagesDTO.setApplication(application); + return applicationPagesDTO; + } + + private static @NotNull PageNameIdDTO getPageNameIdDTO(NewPage pageFromDb, String homePageId, boolean viewMode) { + PageNameIdDTO pageNameIdDTO = new PageNameIdDTO(); + pageNameIdDTO.setId(pageFromDb.getId()); + + if (pageFromDb.getDefaultResources() == null) { + throw new AppsmithException(AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "page", pageFromDb.getId()); + } + + pageNameIdDTO.setDefaultPageId(pageFromDb.getDefaultResources().getPageId()); + PageDTO pageDTO; + + if (Boolean.TRUE.equals(viewMode)) { + if (pageFromDb.getPublishedPage() == null) { + // We are trying to fetch published pages, however; + // it doesn't exist because the page hasn't been published yet + throw new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.PAGE, pageFromDb.getId()); + } + pageDTO = pageFromDb.getPublishedPage(); + } else { + pageDTO = pageFromDb.getUnpublishedPage(); + } - return Mono.zip(applicationMono, pagesListMono).map(tuple -> { - log.debug("Populating applicationPagesDTO ..."); - Application application = tuple.getT1(); - application.setPages(null); - application.setPublishedPages(null); - application.setViewMode(view); - List nameIdDTOList = tuple.getT2(); - ApplicationPagesDTO applicationPagesDTO = new ApplicationPagesDTO(); - applicationPagesDTO.setWorkspaceId(application.getWorkspaceId()); - applicationPagesDTO.setPages(nameIdDTOList); - applicationPagesDTO.setApplication(application); - return applicationPagesDTO; - }); + pageNameIdDTO.setName(pageDTO.getName()); + pageNameIdDTO.setIsHidden(pageDTO.getIsHidden()); + pageNameIdDTO.setSlug(pageDTO.getSlug()); + pageNameIdDTO.setIcon(pageDTO.getIcon()); + pageNameIdDTO.setCustomSlug(pageDTO.getCustomSlug()); + pageNameIdDTO.setUserPermissions(pageFromDb.getUserPermissions()); + pageNameIdDTO.setIsDefault(pageNameIdDTO.getId().equals(homePageId)); + return pageNameIdDTO; } public Mono findApplicationPagesByApplicationIdViewModeAndBranch( 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 index 041056e4900a..e18b0eb80cbf 100644 --- 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 @@ -4,8 +4,11 @@ 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.Application; import com.appsmith.server.domains.ApplicationMode; +import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Plugin; import com.appsmith.server.dtos.ApplicationPagesDTO; import com.appsmith.server.dtos.ConsolidatedAPIResponseDTO; @@ -84,6 +87,7 @@ public class ConsolidatedAPIServiceImpl implements ConsolidatedAPIService { private final ActionCollectionService actionCollectionService; private final ThemeService themeService; private final ApplicationPageService applicationPageService; + private final ApplicationService applicationService; private final CustomJSLibService customJSLibService; private final PluginService pluginService; private final DatasourceService datasourceService; @@ -199,14 +203,31 @@ public Mono getConsolidatedInfoForPageLoad( applicationIdMonoCache = Mono.just(applicationId).cache(); } - /* Get all pages in application */ - Mono> applicationPagesDTOResponseDTOMonoCache = applicationIdMonoCache - .flatMap(appId -> newPageService.findApplicationPages(appId, null, branchName, mode)) + // dslMigration-over-here using the branchName and defaultId + Mono branchedApplicationMonoCached = applicationIdMonoCache + .flatMap(defaultApplicationId -> applicationService.findByDefaultIdBranchNameAndApplicationMode( + defaultApplicationId, branchName, mode)) + .cache(); + + Mono> pagesFromCurrentApplicationMonoCached = branchedApplicationMonoCached + .flatMap(branchedApplication -> + applicationPageService.getPagesBasedOnApplicationMode(branchedApplication, mode)) + .cache(); + + /* Get all applicationPages in application */ + Mono> applicationPagesDTOResponseDTOMonoCache = Mono.zip( + branchedApplicationMonoCached, pagesFromCurrentApplicationMonoCached) + .flatMap(tuple2 -> { + Application branchedApplication = tuple2.getT1(); + List newPages = tuple2.getT2(); + return newPageService.createApplicationPagesDTO(branchedApplication, newPages, isViewMode, true); + }) .as(this::toResponseDTO) .doOnSuccess(consolidatedAPIResponseDTO::setPages) .name(getQualifiedSpanName(PAGES_SPAN, mode)) .tap(Micrometer.observation(observationRegistry)) .cache(); + fetches.add(applicationPagesDTOResponseDTOMonoCache); /* Get current theme */ @@ -237,8 +258,9 @@ public Mono getConsolidatedInfoForPageLoad( if (!isBlank(defaultPageId)) { /* Get current page */ - fetches.add(applicationPageService - .getPageAndMigrateDslByBranchAndDefaultPageId(defaultPageId, branchName, isViewMode, true) + fetches.add(pagesFromCurrentApplicationMonoCached + .then(applicationPageService.getPageAndMigrateDslByBranchAndDefaultPageId( + defaultPageId, branchName, isViewMode, true)) .as(this::toResponseDTO) .doOnSuccess(consolidatedAPIResponseDTO::setPageWithMigratedDsl) .name(getQualifiedSpanName(CURRENT_PAGE_SPAN, mode)) @@ -296,12 +318,9 @@ public Mono getConsolidatedInfoForPageLoad( .tap(Micrometer.observation(observationRegistry))); /* Get all pages in edit mode post apply migrate DSL changes */ - fetches.add(applicationPagesDTOResponseDTOMonoCache - .map(ResponseDTO::getData) - .map(ApplicationPagesDTO::getPages) + fetches.add(pagesFromCurrentApplicationMonoCached .flatMapMany(Flux::fromIterable) - .flatMap(page -> applicationPageService.getPageAndMigrateDslByBranchAndDefaultPageId( - page.getDefaultPageId(), branchName, false, true)) + .flatMap(page -> applicationPageService.getPageDTOAfterMigratingDSL(page, false, true)) .collect(Collectors.toList()) .as(this::toResponseDTO) .doOnSuccess(consolidatedAPIResponseDTO::setPagesWithMigratedDsl) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCE.java index 25c150113543..da75547982e4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCE.java @@ -2,6 +2,7 @@ import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.User; import com.appsmith.server.dtos.ApplicationPagesDTO; @@ -9,6 +10,7 @@ import com.appsmith.server.dtos.PageDTO; import reactor.core.publisher.Mono; +import java.util.List; import java.util.Optional; public interface ApplicationPageServiceCE { @@ -70,4 +72,9 @@ Mono deleteUnpublishedPageWithOptionalPermission( Mono createOrUpdateSuffixedApplication(Application application, String name, int suffix); int getEvaluationVersion(); + + Mono> getPagesBasedOnApplicationMode( + Application branchedApplication, ApplicationMode applicationMode); + + Mono getPageDTOAfterMigratingDSL(NewPage newPage, boolean viewMode, boolean migrateDsl); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java index c5e68b488078..823c1dc6e886 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java @@ -34,6 +34,7 @@ import com.appsmith.server.dtos.PluginTypeAndCountDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.helpers.CollectionUtils; import com.appsmith.server.helpers.DSLMigrationUtils; import com.appsmith.server.helpers.GitFileUtils; import com.appsmith.server.helpers.GitUtils; @@ -281,23 +282,110 @@ public Mono getPage(String pageId, boolean viewMode) { } @Override - public Mono getPageAndMigrateDslByBranchAndDefaultPageId( - String defaultPageId, String branchName, boolean viewMode, boolean migrateDsl) { - // Fetch the page with read permission in both editor and in viewer. + public Mono> getPagesBasedOnApplicationMode( + Application branchedApplication, ApplicationMode applicationMode) { + + Boolean viewMode = ApplicationMode.PUBLISHED.equals(applicationMode) ? Boolean.TRUE : Boolean.FALSE; + + List applicationPages = Boolean.TRUE.equals(viewMode) + ? branchedApplication.getPublishedPages() + : branchedApplication.getPages(); + + Set pageIds = + applicationPages.stream().map(ApplicationPage::getId).collect(Collectors.toSet()); + return newPageService - .findByBranchNameAndDefaultPageId(branchName, defaultPageId, pagePermission.getReadPermission()) - .flatMap(newPage -> { - return sendPageViewAnalyticsEvent(newPage, viewMode) - .then(getPage(newPage, viewMode)) - .zipWith(Mono.just(newPage)); + .findNewPagesByApplicationId(branchedApplication.getId(), pagePermission.getReadPermission()) + .filter(newPage -> pageIds.contains(newPage.getId())) + .collectList() + .flatMap(newPageList -> { + if (Boolean.TRUE.equals(viewMode)) { + return Mono.just(newPageList); + } + + // autocommit if migration is required + return migrateSchemasForGitConnectedApps(branchedApplication, newPageList) + .onErrorResume(error -> { + log.debug( + "Skipping the autocommit for applicationId : {} due to error; {}", + branchedApplication.getId(), + error.getMessage()); + + return Mono.just(Boolean.FALSE); + }) + .thenReturn(newPageList); + }); + } + + /** + * Publishes the autocommit if it's eligible for one + * @param application : the branched application which requires schemaMigration + * @param newPages : list of pages from db + * @return : a boolean publisher + */ + private Mono migrateSchemasForGitConnectedApps(Application application, List newPages) { + + if (CollectionUtils.isNullOrEmpty(newPages)) { + return Mono.just(Boolean.FALSE); + } + + if (application.getGitArtifactMetadata() == null) { + return Mono.just(Boolean.FALSE); + } + + GitArtifactMetadata gitMetadata = application.getGitArtifactMetadata(); + String defaultApplicationId = gitMetadata.getDefaultArtifactId(); + String branchName = gitMetadata.getBranchName(); + + if (!StringUtils.hasText(branchName)) { + log.debug( + "Skipping the autocommit for applicationId : {}, branch name is not present", application.getId()); + return Mono.just(Boolean.FALSE); + } + + if (!StringUtils.hasText(defaultApplicationId)) { + log.debug( + "Skipping the autocommit for applicationId : {}, defaultApplicationId is not present", + application.getId()); + return Mono.just(Boolean.FALSE); + } + + // since this method is only called when the app is in edit mode + Mono pageDTOMono = getPage(newPages.get(0), false); + return pageDTOMono + .zipWith(dslMigrationUtils.getLatestDslVersion()) + .onErrorMap(throwable -> { + log.error("Error fetching latest DSL version", throwable); + return new AppsmithException(AppsmithError.RTS_SERVER_ERROR, "Error fetching latest DSL version"); }) - .flatMap(objects -> { - PageDTO pageDTO = objects.getT1(); + .flatMap(tuple2 -> { + PageDTO pageDTO = tuple2.getT1(); + Integer latestDslVersion = tuple2.getT2(); + // ensuring that the page has only one layout, as we don't support multiple layouts yet + // when multiple layouts are supported, this code will have to be updated + assert pageDTO.getLayouts().size() == 1; + Layout layout = pageDTO.getLayouts().get(0); + JSONObject layoutDsl = layout.getDsl(); + + boolean isMigrationRequired = GitUtils.isMigrationRequired(layoutDsl, latestDslVersion); + if (isMigrationRequired) { + // Triggering the autocommit + return gitAutoCommitHelper.autoCommitApplication(defaultApplicationId, branchName); + } + + return Mono.just(Boolean.FALSE); + }); + } + + @Override + public Mono getPageDTOAfterMigratingDSL(NewPage newPage, boolean viewMode, boolean migrateDsl) { + return sendPageViewAnalyticsEvent(newPage, viewMode) + .then(getPage(newPage, viewMode)) + .flatMap(pageDTO -> { if (migrateDsl) { // Call the DSL Utils for on demand migration of the page. // Based on view mode save the migrated DSL to the database // Migrate the DSL to the latest version if required - NewPage newPage = objects.getT2(); if (pageDTO.getLayouts() != null) { return migrateAndUpdatePageDsl(newPage, pageDTO, viewMode); } @@ -307,6 +395,15 @@ public Mono getPageAndMigrateDslByBranchAndDefaultPageId( .map(responseUtils::updatePageDTOWithDefaultResources); } + @Override + public Mono getPageAndMigrateDslByBranchAndDefaultPageId( + String defaultPageId, String branchName, boolean viewMode, boolean migrateDsl) { + // Fetch the page with read permission in both editor and in viewer. + return newPageService + .findByBranchNameAndDefaultPageId(branchName, defaultPageId, pagePermission.getReadPermission()) + .flatMap(newPage -> getPageDTOAfterMigratingDSL(newPage, viewMode, migrateDsl)); + } + private Mono migrateAndUpdatePageDsl(NewPage newPage, PageDTO page, boolean viewMode) { return dslMigrationUtils .getLatestDslVersion() @@ -323,25 +420,15 @@ private Mono migrateAndUpdatePageDsl(NewPage newPage, PageDTO page, boo JSONObject layoutDsl = layout.getDsl(); boolean isMigrationRequired = GitUtils.isMigrationRequired(layoutDsl, latestDslVersion); if (isMigrationRequired) { - // if edit mode, then trigger the auto commit event - Mono autoCommitEventRunner; - if (!viewMode) { - autoCommitEventRunner = gitAutoCommitHelper.autoCommitApplication( - newPage.getDefaultResources().getApplicationId(), - newPage.getDefaultResources().getBranchName()); - } else { - autoCommitEventRunner = Mono.just(Boolean.FALSE); - } - // zipping them so that they can run in parallel - return Mono.zip(dslMigrationUtils.migratePageDsl(layoutDsl), autoCommitEventRunner) + return dslMigrationUtils + .migratePageDsl(layoutDsl) .onErrorMap(throwable -> { log.error("Error while migrating DSL ", throwable); return new AppsmithException( AppsmithError.RTS_SERVER_ERROR, "Error while migrating to latest DSL version"); }) - .flatMap(tuple2 -> { - JSONObject migratedDsl = tuple2.getT1(); + .flatMap(migratedDsl -> { // update the current page DTO with migrated dsl page.getLayouts().get(0).setDsl(migratedDsl); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationPageServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationPageServiceTest.java index da67d49781a7..30bf562bf590 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationPageServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationPageServiceTest.java @@ -5,6 +5,7 @@ import com.appsmith.server.applications.base.ApplicationService; import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.GitArtifactMetadata; import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.NewPage; @@ -41,7 +42,9 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -431,4 +434,63 @@ public void createApplicationWithId_throwsException() { && throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.ID, id))) .verify(); } + + @Test + @WithUserDetails("api_user") + public void verifyGetPagesBasedOnApplicationMode_ReturnsRigthNumberOfPages_BasedOnApplicationMode() { + final String appName = "app" + UUID.randomUUID(); + Application application = new Application(); + application.setName(appName); + + Application createdApplication = applicationPageService + .createApplication(application, workspace.getId()) + .block(); + + String applicationId = createdApplication.getId(); + + PageDTO pageDTO = new PageDTO(); + pageDTO.setApplicationId(applicationId); + final String pageName = "app" + UUID.randomUUID(); + pageDTO.setName(pageName); + applicationPageService.createPage(pageDTO).block(); + + applicationPageService.publish(applicationId, true).block(); + + PageDTO pageDTO1 = new PageDTO(); + pageDTO1.setApplicationId(applicationId); + final String unpublishedPageName = "app" + UUID.randomUUID(); + pageDTO1.setName(unpublishedPageName); + applicationPageService.createPage(pageDTO1).block(); + + Application updatedApplication = + applicationService.findById(createdApplication.getId()).block(); + + Mono> unpublishedPagesMono = + applicationPageService.getPagesBasedOnApplicationMode(updatedApplication, ApplicationMode.EDIT); + + Mono> publishedPagesMono = + applicationPageService.getPagesBasedOnApplicationMode(updatedApplication, ApplicationMode.PUBLISHED); + + StepVerifier.create(publishedPagesMono) + .assertNext(pages -> { + assertThat(pages.size()).isEqualTo(2); + Set pageNames = pages.stream() + .map(page -> page.getUnpublishedPage().getName()) + .collect(Collectors.toSet()); + assertThat(pageNames).contains(pageName); + assertThat(pageNames).doesNotContain(unpublishedPageName); + }) + .verifyComplete(); + + StepVerifier.create(unpublishedPagesMono) + .assertNext(pages -> { + assertThat(pages.size()).isEqualTo(3); + Set pageNames = pages.stream() + .map(page -> page.getUnpublishedPage().getName()) + .collect(Collectors.toSet()); + assertThat(pageNames).contains(pageName); + assertThat(pageNames).contains(unpublishedPageName); + }) + .verifyComplete(); + } } 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 index 2bd0313d1c42..1beada6d1295 100644 --- 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 @@ -6,8 +6,10 @@ 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.Application; import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.CustomJSLib; +import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.Tenant; import com.appsmith.server.domains.Theme; @@ -214,8 +216,18 @@ public void testPageLoadResponseForViewMode() { ApplicationPagesDTO sampleApplicationPagesDTO = new ApplicationPagesDTO(); sampleApplicationPagesDTO.setWorkspaceId("sampleWorkspaceId"); - when(spyNewPageService.findApplicationPages(anyString(), any(), anyString(), any())) - .thenReturn(Mono.just(sampleApplicationPagesDTO)); + + doReturn(Mono.just(new Application())) + .when(spyApplicationService) + .findByDefaultIdBranchNameAndApplicationMode(anyString(), anyString(), any()); + + doReturn(Mono.just(List.of(new NewPage()))) + .when(spyApplicationPageService) + .getPagesBasedOnApplicationMode(any(), any()); + + doReturn(Mono.just(sampleApplicationPagesDTO)) + .when(spyNewPageService) + .createApplicationPagesDTO(any(), any(), anyBoolean(), anyBoolean()); Theme sampleTheme = new Theme(); sampleTheme.setName("sampleTheme"); @@ -385,8 +397,18 @@ public void testPageLoadResponseForEditMode() { ApplicationPagesDTO sampleApplicationPagesDTO = new ApplicationPagesDTO(); sampleApplicationPagesDTO.setWorkspaceId("sampleWorkspaceId"); - when(spyNewPageService.findApplicationPages(anyString(), any(), anyString(), any())) - .thenReturn(Mono.just(sampleApplicationPagesDTO)); + + doReturn(Mono.just(new Application())) + .when(spyApplicationService) + .findByDefaultIdBranchNameAndApplicationMode(anyString(), anyString(), any()); + + doReturn(Mono.just(List.of(new NewPage()))) + .when(spyApplicationPageService) + .getPagesBasedOnApplicationMode(any(), any()); + + doReturn(Mono.just(sampleApplicationPagesDTO)) + .when(spyNewPageService) + .createApplicationPagesDTO(any(), any(), anyBoolean(), anyBoolean()); Theme sampleTheme = new Theme(); sampleTheme.setName("sampleTheme"); @@ -406,6 +428,11 @@ public void testPageLoadResponseForEditMode() { .when(spyApplicationPageService) .getPageAndMigrateDslByBranchAndDefaultPageId(anyString(), anyString(), anyBoolean(), anyBoolean()); + doReturn(Mono.just(samplePageDTO)) + .doReturn(Mono.just(samplePageDTO)) + .when(spyApplicationPageService) + .getPageDTOAfterMigratingDSL(any(), anyBoolean(), anyBoolean()); + ActionDTO sampleActionDTO = new ActionDTO(); sampleActionDTO.setName("sampleActionDTO"); sampleActionDTO.setUpdatedAt(Instant.now()); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java index 9934086e3d92..c0dc3487112a 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java @@ -10,6 +10,8 @@ import com.appsmith.server.domains.Workspace; import com.appsmith.server.dtos.ApplicationPagesDTO; import com.appsmith.server.dtos.PageDTO; +import com.appsmith.server.dtos.PageNameIdDTO; +import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.TextUtils; import com.appsmith.server.newpages.base.NewPageService; @@ -18,6 +20,7 @@ import com.appsmith.server.repositories.PermissionGroupRepository; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.PagePermission; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,12 +34,15 @@ import reactor.test.StepVerifier; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +@Slf4j @SpringBootTest @ExtendWith(SpringExtension.class) public class NewPageServiceTest { @@ -313,4 +319,91 @@ public void publishPages_WhenUserDoesNotHavePermissionOnPages_NotPublished() { }) .verifyComplete(); } + + @Test + @WithUserDetails("api_user") + public void verifyCreateApplicationPagesDTO_ReturnsRightNumberOfPages_BasedOnViewMode() { + final String appName = "app" + UUID.randomUUID(); + Application application = new Application(); + application.setName(appName); + + String applicationId = applicationPageService + .createApplication(application, workspaceId) + .block() + .getId(); + + PageDTO pageDTO = new PageDTO(); + pageDTO.setApplicationId(applicationId); + final String pageName = "app" + UUID.randomUUID(); + pageDTO.setName(pageName); + applicationPageService.createPage(pageDTO).block(); + + applicationPageService.publish(applicationId, true).block(); + + List publishedPages = newPageService + .findNewPagesByApplicationId(applicationId, pagePermission.getReadPermission()) + .collectList() + .block(); + + PageDTO pageDTO1 = new PageDTO(); + pageDTO1.setApplicationId(applicationId); + final String unpublishedPageName = "app" + UUID.randomUUID(); + pageDTO1.setName(unpublishedPageName); + applicationPageService.createPage(pageDTO1).block(); + + Application updatedApplication = + applicationService.findById(applicationId).block(); + + List allPages = newPageService + .findNewPagesByApplicationId(applicationId, pagePermission.getReadPermission()) + .collectList() + .block(); + + Mono applicationPagesDTOMono = + newPageService.createApplicationPagesDTO(updatedApplication, allPages, false, true); + + StepVerifier.create(applicationPagesDTOMono) + .assertNext(applicationPagesDTO -> { + assertThat(applicationPagesDTO.getWorkspaceId()).isEqualTo(workspaceId); + assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo(updatedApplication.getName()); + assertThat(applicationPagesDTO.getApplication().getId()).isEqualTo(updatedApplication.getId()); + + assertThat(applicationPagesDTO.getPages().size()).isEqualTo(3); + Map pageNameIdMap = applicationPagesDTO.getPages().stream() + .collect(Collectors.toMap(PageNameIdDTO::getName, Function.identity())); + + assertThat(pageNameIdMap.keySet()).contains(unpublishedPageName); + assertThat(pageNameIdMap.keySet()).contains(pageName); + assertThat(pageNameIdMap.get("Page1").getIsDefault()).isTrue(); + }) + .verifyComplete(); + + Mono viewModeTrueApplicationPagesDTOMono = newPageService.createApplicationPagesDTO( + applicationService.findById(applicationId).block(), publishedPages, true, true); + + StepVerifier.create(viewModeTrueApplicationPagesDTOMono) + .assertNext(applicationPagesDTO -> { + assertThat(applicationPagesDTO.getWorkspaceId()).isEqualTo(workspaceId); + assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo(updatedApplication.getName()); + assertThat(applicationPagesDTO.getApplication().getId()).isEqualTo(updatedApplication.getId()); + + assertThat(applicationPagesDTO.getPages().size()).isEqualTo(2); + Map pageNameIdMap = applicationPagesDTO.getPages().stream() + .collect(Collectors.toMap(PageNameIdDTO::getName, Function.identity())); + + assertThat(pageNameIdMap.keySet()).doesNotContain(unpublishedPageName); + assertThat(pageNameIdMap.keySet()).contains(pageName); + assertThat(pageNameIdMap.get("Page1").getIsDefault()).isTrue(); + }) + .verifyComplete(); + + Mono viewModeTrueMono = newPageService.createApplicationPagesDTO( + applicationService.findById(applicationId).block(), allPages, true, true); + + StepVerifier.create(viewModeTrueMono).verifyErrorSatisfies(error -> { + assertThat(error).isInstanceOf(AppsmithException.class); + assertThat(((AppsmithException) error).getAppErrorCode()) + .isEqualTo(AppsmithError.ACL_NO_RESOURCE_FOUND.getAppErrorCode()); + }); + } }