PageController.java
/*
* Copyright (C) 2023 B3Partners B.V.
*
* SPDX-License-Identifier: MIT
*/
package org.tailormap.api.controller;
import static org.springframework.beans.BeanUtils.copyProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.server.ResponseStatusException;
import org.tailormap.api.annotation.AppRestController;
import org.tailormap.api.persistence.Configuration;
import org.tailormap.api.persistence.Page;
import org.tailormap.api.persistence.Upload;
import org.tailormap.api.persistence.helper.UploadHelper;
import org.tailormap.api.persistence.json.MenuItem;
import org.tailormap.api.persistence.json.PageTile;
import org.tailormap.api.repository.ApplicationRepository;
import org.tailormap.api.repository.ConfigurationRepository;
import org.tailormap.api.repository.PageRepository;
import org.tailormap.api.security.AuthorisationService;
import org.tailormap.api.viewer.model.PageResponse;
import org.tailormap.api.viewer.model.ViewerMenuItem;
import org.tailormap.api.viewer.model.ViewerPageTile;
@AppRestController
public class PageController {
private final ConfigurationRepository configurationRepository;
private final ApplicationRepository applicationRepository;
private final AuthorisationService authorisationService;
private final PageRepository pageRepository;
private final UploadHelper uploadHelper;
public PageController(
ConfigurationRepository configurationRepository,
ApplicationRepository applicationRepository,
AuthorisationService authorisationService,
PageRepository pageRepository,
UploadHelper uploadHelper) {
this.configurationRepository = configurationRepository;
this.applicationRepository = applicationRepository;
this.pageRepository = pageRepository;
this.authorisationService = authorisationService;
this.uploadHelper = uploadHelper;
}
private ResponseStatusException notFound() {
return new ResponseStatusException(HttpStatus.NOT_FOUND);
}
@GetMapping(path = "${tailormap-api.base-path}/page")
public PageResponse homePage() {
Page page = configurationRepository
.findByKey(Configuration.HOME_PAGE)
.map(Configuration::getValue)
.map(Long::parseLong)
.flatMap(pageRepository::findById)
.orElseThrow(this::notFound);
return getPageResponse(page);
}
@GetMapping(path = {"${tailormap-api.base-path}/page/{name}"})
public PageResponse page(@PathVariable(required = false) String name) {
return pageRepository.findByName(name).map(this::getPageResponse).orElseThrow(this::notFound);
}
private static class ViewerPageTileResult {
ViewerPageTile viewerPageTile;
boolean shouldBeFiltered;
}
private PageResponse getPageResponse(Page page) {
PageResponse pageResponse = new PageResponse();
if (!authorisationService.userAllowedToViewPage(page)) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
copyProperties(page, pageResponse);
pageResponse.tiles(page.getTiles().stream()
.map(this::convert)
.filter(viewerPageTileResult -> !viewerPageTileResult.shouldBeFiltered)
.map(viewerPageTileResult -> viewerPageTileResult.viewerPageTile)
.toList());
List<MenuItem> menuItems = configurationRepository
.findByKey(Configuration.PORTAL_MENU)
.map(Configuration::getJsonValue)
.filter(JsonNode::isArray)
.map(jsonNode -> {
try {
return Arrays.asList(new ObjectMapper().treeToValue(jsonNode, MenuItem[].class));
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, null, e);
}
})
.orElse(Collections.emptyList());
List<ViewerMenuItem> viewerMenuItems = menuItems.stream()
.filter(menuItem -> menuItem.getExclusiveOnPageId() == null
|| menuItem.getExclusiveOnPageId().equals(page.getId()))
.map(menuItem -> {
ViewerMenuItem viewerMenuItem = new ViewerMenuItem();
copyProperties(menuItem, viewerMenuItem);
Optional.ofNullable(menuItem.getPageId())
.flatMap(pageRepository::findById)
.ifPresent(linkedPage -> viewerMenuItem.pageUrl("/page/" + linkedPage.getName()));
return viewerMenuItem;
})
.toList();
pageResponse.setMenu(viewerMenuItems);
return pageResponse;
}
/**
* @param tile The page tile configuration
* @return A page tile for the viewer with a boolean set to whether the tile should not be shown (filtered).
*/
private ViewerPageTileResult convert(PageTile tile) {
ViewerPageTile viewerPageTile = new ViewerPageTile();
ViewerPageTileResult result = new ViewerPageTileResult();
result.viewerPageTile = viewerPageTile;
result.shouldBeFiltered = false;
copyProperties(tile, viewerPageTile);
PageTile.TileTypeEnum tileType = getTileTypeForPageTile(tile);
if (tileType == PageTile.TileTypeEnum.APPLICATION) {
Optional.ofNullable(tile.getApplicationId())
.flatMap(applicationRepository::findById)
.filter(application -> !Boolean.TRUE.equals(tile.getFilterRequireAuthorization())
|| authorisationService.userAllowedToViewApplication(application))
.ifPresentOrElse(
application -> {
viewerPageTile.applicationUrl("/app/" + application.getName());
viewerPageTile.requiresLogin(
!authorisationService.userAllowedToViewApplication(application));
},
() -> result.shouldBeFiltered = true);
}
if (tileType == PageTile.TileTypeEnum.PAGE) {
Optional.ofNullable(tile.getPageId())
.flatMap(pageRepository::findById)
.filter(page -> !Boolean.TRUE.equals(tile.getFilterRequireAuthorization())
|| authorisationService.userAllowedToViewPage(page))
.ifPresentOrElse(
page -> {
viewerPageTile.pageUrl("/page/" + page.getName());
viewerPageTile.requiresLogin(!authorisationService.userAllowedToViewPage(page));
},
() -> result.shouldBeFiltered = true);
}
if (tileType == PageTile.TileTypeEnum.URL
&& !tile.getAuthorizationRules().isEmpty()
&& !authorisationService.userAllowedToViewPageTile(tile)) {
result.shouldBeFiltered = true;
}
viewerPageTile.image(uploadHelper.getUrlForImage(tile.getImage(), Upload.CATEGORY_PORTAL_IMAGE));
return result;
}
private PageTile.TileTypeEnum getTileTypeForPageTile(PageTile pageTile) {
if (pageTile.getTileType() != null) {
return pageTile.getTileType();
}
if (pageTile.getApplicationId() != null) {
return PageTile.TileTypeEnum.APPLICATION;
}
if (pageTile.getPageId() != null) {
return PageTile.TileTypeEnum.PAGE;
}
if (pageTile.getUrl() != null) {
return PageTile.TileTypeEnum.URL;
}
return PageTile.TileTypeEnum.APPLICATION;
}
}