From a103461b1dee28c72e37192146325ad24f12317e Mon Sep 17 00:00:00 2001 From: milanhorvath <35398860+milanhorvath@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:48:33 +0100 Subject: [PATCH] Feat/tenant (#4) * remove custom profiles from test * fix: update external data * feat: add tenantId to model and enable it * legacy and external tenant test * update test to have 100% * feat: add tenant tests * fix: remove code smell --------- Co-authored-by: milan.horvath --- pom.xml | 16 +- src/main/helm/Chart.yaml | 2 +- .../workspace/domain/daos/MenuItemDAO.java | 24 +- .../workspace/domain/daos/ProductDAO.java | 47 +- .../workspace/domain/daos/WorkspaceDAO.java | 26 +- .../domain/di/WorkspaceDataImportService.java | 84 +--- .../domain/di/WorkspaceImportService.java | 108 +++++ .../mappers/WorkspaceDataImportMapperV1.java | 3 + .../workspace/domain/models/MenuItem.java | 7 +- .../domain/models/Microfrontend.java | 2 +- .../workspace/domain/models/Product.java | 7 +- .../workspace/domain/models/Workspace.java | 2 + .../WorkspaceExternalV1RestController.java | 2 - .../MenuInternalRestController.java | 10 +- .../ProductInternalRestController.java | 7 +- .../WorkspaceInternalRestController.java | 10 +- .../rs/internal/mappers/MenuItemMapper.java | 3 + .../rs/internal/mappers/ProductMapper.java | 17 + .../PortalLegacyRestController.java | 2 - .../controllers/TkitLegacyAppConfig.java | 11 + .../controllers/TkitPortalRestController.java | 8 +- .../rs/legacy/mappers/TkitPortalMapper.java | 1 + src/main/resources/application.properties | 33 ++ .../db/v1/2023-11-09-create-tables.xml | 16 +- .../workspace/AfterStartDataImportTest.java | 32 ++ .../domain/daos/MenuItemDAOTest.java | 2 + .../workspace/domain/daos/ProductDAOTest.java | 4 + .../domain/daos/WorkspaceDAOTest.java | 2 + ...rkspaceDataImportServiceExceptionTest.java | 5 + .../WorkspaceDataImportServiceFileTest.java | 53 --- .../di/WorkspaceDataImportServiceTest.java | 15 +- .../WorkspaceExternalV1RestControllerIT.java | 3 +- ...spaceExternalV1RestControllerTenantIT.java | 7 + ...aceExternalV1RestControllerTenantTest.java | 51 ++ ...WorkspaceExternalV1RestControllerTest.java | 2 +- .../MenuInternalRestControllerTenantIT.java | 8 + .../MenuInternalRestControllerTenantTest.java | 450 ++++++++++++++++++ .../MenuInternalRestControllerTest.java | 28 +- .../ProductRestControllerTenantIT.java | 8 + .../ProductRestControllerTenantTest.java | 314 ++++++++++++ .../ProductRestControllerTest.java | 48 +- ...rkspaceInternalRestControllerTenantIT.java | 8 + ...spaceInternalRestControllerTenantTest.java | 322 +++++++++++++ .../WorkspaceInternalRestControllerTest.java | 25 +- .../PortalLegacyRestControllerTenantIT.java | 8 + .../PortalLegacyRestControllerTenantTest.java | 95 ++++ .../TkitPortalRestControllerTenantIT.java | 8 + .../TkitPortalRestControllerTenantTest.java | 128 +++++ ...TkitPortalSubmitMenuRequestTenantTest.java | 173 +++++++ .../TkitPortalSubmitMenuRequestTest.java | 30 +- .../onecx/workspace/test/AbstractTest.java | 29 ++ src/test/resources/data/testdata-external.xml | 15 +- src/test/resources/data/testdata-internal.xml | 64 +-- src/test/resources/data/testdata-legacy.xml | 50 +- .../resources/import/workspace-import.json | 123 +++-- 55 files changed, 2255 insertions(+), 303 deletions(-) create mode 100644 src/main/java/io/github/onecx/workspace/domain/di/WorkspaceImportService.java create mode 100644 src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitLegacyAppConfig.java create mode 100644 src/test/java/io/github/onecx/workspace/AfterStartDataImportTest.java delete mode 100644 src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceFileTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantIT.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantIT.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantIT.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantIT.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantIT.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantIT.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantTest.java create mode 100644 src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTenantTest.java diff --git a/pom.xml b/pom.xml index 0eb1281..befaf12 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ io.github.onecx onecx-quarkus3-parent - 0.22.0 + 0.23.0 onecx-workspace-svc @@ -40,6 +40,20 @@ tkit-quarkus-rest + + + io.github.onecx.quarkus + onecx-tenant + + + org.tkit.quarkus.lib + tkit-quarkus-jpa-tenant + + + org.tkit.quarkus.lib + tkit-quarkus-rest-context + + io.quarkus diff --git a/src/main/helm/Chart.yaml b/src/main/helm/Chart.yaml index 0e97809..1adcd41 100644 --- a/src/main/helm/Chart.yaml +++ b/src/main/helm/Chart.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v2 name: onecx-workspace-svc version: 0.0.0 appVersion: 0.0.0 diff --git a/src/main/java/io/github/onecx/workspace/domain/daos/MenuItemDAO.java b/src/main/java/io/github/onecx/workspace/domain/daos/MenuItemDAO.java index 69f1955..310a1a0 100644 --- a/src/main/java/io/github/onecx/workspace/domain/daos/MenuItemDAO.java +++ b/src/main/java/io/github/onecx/workspace/domain/daos/MenuItemDAO.java @@ -6,6 +6,7 @@ import java.util.List; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.NoResultException; import jakarta.persistence.criteria.Join; import jakarta.transaction.Transactional; @@ -13,10 +14,7 @@ import org.tkit.quarkus.jpa.exceptions.DAOException; import org.tkit.quarkus.jpa.models.TraceableEntity_; -import io.github.onecx.workspace.domain.models.MenuItem; -import io.github.onecx.workspace.domain.models.MenuItem_; -import io.github.onecx.workspace.domain.models.Workspace; -import io.github.onecx.workspace.domain.models.Workspace_; +import io.github.onecx.workspace.domain.models.*; @ApplicationScoped public class MenuItemDAO extends AbstractDAO { @@ -168,7 +166,25 @@ public List loadAllMenuItemsByWorkspaceId(String workspaceId) { } } + @Override + public MenuItem findById(Object id) throws DAOException { + try { + var cb = this.getEntityManager().getCriteriaBuilder(); + var cq = cb.createQuery(MenuItem.class); + var root = cq.from(MenuItem.class); + cq.where(cb.equal(root.get(TraceableEntity_.ID), id)); + return this.getEntityManager().createQuery(cq).getSingleResult(); + } catch (NoResultException nre) { + return null; + } catch (Exception e) { + throw new DAOException(MenuItemDAO.ErrorKeys.FIND_ENTITY_BY_ID_FAILED, e, entityName, id); + } + } + public enum ErrorKeys { + + FIND_ENTITY_BY_ID_FAILED, + ERROR_UPDATE_MENU_ITEMS, ERROR_LOAD_ALL_MENU_ITEMS_BY_WORKSPACE_ID, ERROR_DELETE_ALL_MENU_ITEMS_BY_WORKSPACE_ID, diff --git a/src/main/java/io/github/onecx/workspace/domain/daos/ProductDAO.java b/src/main/java/io/github/onecx/workspace/domain/daos/ProductDAO.java index 1bf4265..55b68d1 100644 --- a/src/main/java/io/github/onecx/workspace/domain/daos/ProductDAO.java +++ b/src/main/java/io/github/onecx/workspace/domain/daos/ProductDAO.java @@ -3,12 +3,14 @@ import java.util.List; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.NoResultException; import jakarta.transaction.Transactional; import org.tkit.quarkus.jpa.daos.AbstractDAO; import org.tkit.quarkus.jpa.exceptions.DAOException; import org.tkit.quarkus.jpa.models.TraceableEntity_; +import io.github.onecx.workspace.domain.models.MenuItem_; import io.github.onecx.workspace.domain.models.Product; import io.github.onecx.workspace.domain.models.Product_; @@ -24,23 +26,60 @@ public List getProductsForWorkspaceId(String id) { cq.where(cb.equal(root.get(Product_.WORKSPACE).get(TraceableEntity_.ID), id)); return this.getEntityManager().createQuery(cq).getResultList(); } catch (Exception ex) { - throw new DAOException(ProductDAO.ErrorKeys.ERROR_FIND_PRODUCTS_BY_WORKSPACE_ID, ex); + throw this.handleConstraint(ex, ProductDAO.ErrorKeys.ERROR_FIND_PRODUCTS_BY_WORKSPACE_ID); } } @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = DAOException.class) public void deleteProduct(String id) { + try { + var cb = this.getEntityManager().getCriteriaBuilder(); + var cq = cb.createQuery(Product.class); + var root = cq.from(Product.class); + + cq.where(cb.equal(root.get(TraceableEntity_.ID), id)); + var product = this.getEntityManager().createQuery(cq).getSingleResult(); + this.getEntityManager().remove(product); + } catch (NoResultException nre) { + // do nothing on No result + } catch (Exception ex) { + throw this.handleConstraint(ex, ProductDAO.ErrorKeys.ERROR_DELETE_PRODUCT_ID); + } + } + + @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = DAOException.class) + public void deleteProductByWorkspaceId(String workspaceId) { var cb = this.getEntityManager().getCriteriaBuilder(); var cq = cb.createQuery(Product.class); var root = cq.from(Product.class); - cq.where(cb.equal(root.get(TraceableEntity_.ID), id)); - var product = this.getEntityManager().createQuery(cq).getSingleResult(); - this.getEntityManager().remove(product); + cq.where(cb.equal(root.get(MenuItem_.WORKSPACE).get(TraceableEntity_.ID), workspaceId)); + + var products = this.getEntityManager().createQuery(cq).getResultList(); + delete(products); + } + + @Override + public Product findById(Object id) throws DAOException { + try { + var cb = this.getEntityManager().getCriteriaBuilder(); + var cq = cb.createQuery(Product.class); + var root = cq.from(Product.class); + cq.where(cb.equal(root.get(TraceableEntity_.ID), id)); + return this.getEntityManager().createQuery(cq).getSingleResult(); + } catch (NoResultException nre) { + return null; + } catch (Exception e) { + throw this.handleConstraint(e, ProductDAO.ErrorKeys.FIND_ENTITY_BY_ID_FAILED); + } } public enum ErrorKeys { + FIND_ENTITY_BY_ID_FAILED, + + ERROR_DELETE_PRODUCT_ID, + ERROR_FIND_PRODUCTS_BY_WORKSPACE_ID, } diff --git a/src/main/java/io/github/onecx/workspace/domain/daos/WorkspaceDAO.java b/src/main/java/io/github/onecx/workspace/domain/daos/WorkspaceDAO.java index 927e9d6..3968d08 100644 --- a/src/main/java/io/github/onecx/workspace/domain/daos/WorkspaceDAO.java +++ b/src/main/java/io/github/onecx/workspace/domain/daos/WorkspaceDAO.java @@ -15,6 +15,7 @@ import org.tkit.quarkus.jpa.daos.Page; import org.tkit.quarkus.jpa.daos.PageResult; import org.tkit.quarkus.jpa.exceptions.DAOException; +import org.tkit.quarkus.jpa.models.TraceableEntity_; import org.tkit.quarkus.jpa.utils.QueryCriteriaUtil; import io.github.onecx.workspace.domain.criteria.WorkspaceSearchCriteria; @@ -25,6 +26,22 @@ @ApplicationScoped public class WorkspaceDAO extends AbstractDAO { + // https://hibernate.atlassian.net/browse/HHH-16830#icft=HHH-16830 + @Override + public Workspace findById(Object id) throws DAOException { + try { + var cb = this.getEntityManager().getCriteriaBuilder(); + var cq = cb.createQuery(Workspace.class); + var root = cq.from(Workspace.class); + cq.where(cb.equal(root.get(TraceableEntity_.ID), id)); + return this.getEntityManager().createQuery(cq).getSingleResult(); + } catch (NoResultException nre) { + return null; + } catch (Exception e) { + throw handleConstraint(e, ErrorKeys.FIND_ENTITY_BY_ID_FAILED); + } + } + /** * This method fetches a workspace with * workspaceName provided as a param and @@ -45,7 +62,7 @@ public Workspace findByWorkspaceName(String workspaceName) { } catch (NoResultException nre) { return null; } catch (Exception ex) { - throw new DAOException(ErrorKeys.ERROR_FIND_WORKSPACE_NAME, ex); + throw handleConstraint(ex, ErrorKeys.ERROR_FIND_WORKSPACE_NAME); } } @@ -73,7 +90,7 @@ public Workspace loadByWorkspaceName(String workspaceName) { } catch (NoResultException nre) { return null; } catch (Exception ex) { - throw new DAOException(ErrorKeys.ERROR_FIND_WORKSPACE_NAME, ex); + throw handleConstraint(ex, ErrorKeys.ERROR_FIND_WORKSPACE_NAME); } } @@ -101,7 +118,7 @@ public PageResult findBySearchCriteria(WorkspaceSearchCriteria criter return createPageQuery(cq, Page.of(criteria.getPageNumber(), criteria.getPageSize())).getPageResult(); } catch (Exception ex) { - throw new DAOException(ErrorKeys.ERROR_FIND_BY_CRITERIA, ex); + throw handleConstraint(ex, ErrorKeys.ERROR_FIND_BY_CRITERIA); } } @@ -124,12 +141,13 @@ public Stream findByThemeName(String themeName) { return this.getEntityManager().createQuery(cq).getResultStream(); } catch (Exception ex) { - throw new DAOException(ErrorKeys.ERROR_FIND_BY_THEME_NAME, ex); + throw handleConstraint(ex, ErrorKeys.ERROR_FIND_BY_THEME_NAME); } } public enum ErrorKeys { + FIND_ENTITY_BY_ID_FAILED, ERROR_FIND_BY_BASE_URL, ERROR_FIND_BY_CRITERIA, diff --git a/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportService.java b/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportService.java index 52a3da9..758c9c8 100644 --- a/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportService.java +++ b/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportService.java @@ -1,7 +1,9 @@ package io.github.onecx.workspace.domain.di; -import java.util.LinkedList; -import java.util.List; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toCollection; + +import java.util.ArrayList; import jakarta.inject.Inject; import jakarta.transaction.Transactional; @@ -13,32 +15,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import gen.io.github.onecx.workspace.di.workspace.v1.model.ImportRequestDTOV1; -import gen.io.github.onecx.workspace.di.workspace.v1.model.MenuItemStructureDTOV1; import gen.io.github.onecx.workspace.di.workspace.v1.model.WorkspaceDataImportDTOV1; -import io.github.onecx.workspace.domain.daos.MenuItemDAO; -import io.github.onecx.workspace.domain.daos.ProductDAO; -import io.github.onecx.workspace.domain.daos.WorkspaceDAO; -import io.github.onecx.workspace.domain.di.mappers.WorkspaceDataImportMapperV1; -import io.github.onecx.workspace.domain.models.MenuItem; -import io.github.onecx.workspace.domain.models.Workspace; +import gen.io.github.onecx.workspace.di.workspace.v1.model.WorkspaceImportDTOV1; @DataImport("workspace") public class WorkspaceDataImportService implements DataImportService { - @Inject - MenuItemDAO menuItemDAO; - - @Inject - WorkspaceDAO workspaceDAO; - - @Inject - ProductDAO productDAO; - @Inject ObjectMapper objectMapper; @Inject - WorkspaceDataImportMapperV1 mapper; + WorkspaceImportService importService; @Override @Transactional(Transactional.TxType.REQUIRES_NEW) @@ -58,71 +45,34 @@ public void importData(DataImportConfig config) { } public void cleanInsert(WorkspaceDataImportDTOV1 data) { - if (data == null) { + if (data == null || data.getRequests() == null) { return; } + var tenantIds = data.getRequests().stream().map(ImportRequestDTOV1::getWorkspace) + .collect(groupingBy(WorkspaceImportDTOV1::getTenantId, toCollection(ArrayList::new))).keySet(); - // clean data - productDAO.deleteAll(); - menuItemDAO.deleteAll(); - workspaceDAO.deleteAll(); + for (var tenantId : tenantIds) { + try { + importService.deleteAll(tenantId); + } catch (Exception ex) { + throw new ImportException("Error deleting data from tenant " + tenantId, ex); + } + } // import portals importRequests(data); } public void importRequests(WorkspaceDataImportDTOV1 data) { - if (data.getRequests() == null) { - return; - } - for (var request : data.getRequests()) { try { - importRequest(request); + importService.importRequest(request); } catch (Exception ex) { throw new ImportException("Error import portal " + request.getWorkspace().getWorkspaceName(), ex); } } } - public void importRequest(ImportRequestDTOV1 importRequestDTO) { - - var dto = importRequestDTO.getWorkspace(); - var workspace = mapper.createWorkspace(dto); - - workspace = workspaceDAO.create(workspace); - - if (importRequestDTO.getMenuItems() != null && !importRequestDTO.getMenuItems().isEmpty()) { - menuItemDAO.deleteAllMenuItemsByWorkspaceId(workspace.getId()); - List menus = new LinkedList<>(); - recursiveMappingTreeStructure(importRequestDTO.getMenuItems(), workspace, null, menus); - menuItemDAO.create(menus); - } - - } - - public void recursiveMappingTreeStructure(List items, Workspace workspace, MenuItem parent, - List mappedItems) { - int position = 0; - for (MenuItemStructureDTOV1 item : items) { - if (item != null) { - MenuItem menu = mapper.mapMenu(item); - menu.setWorkspace(workspace); - menu.setWorkspaceName(workspace.getWorkspaceName()); - menu.setPosition(position); - menu.setParent(parent); - mappedItems.add(menu); - position++; - - if (item.getChildren() == null || item.getChildren().isEmpty()) { - continue; - } - - recursiveMappingTreeStructure(item.getChildren(), workspace, menu, mappedItems); - } - } - } - public static class ImportException extends RuntimeException { public ImportException(String message, Throwable ex) { diff --git a/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceImportService.java b/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceImportService.java new file mode 100644 index 0000000..b26d0f7 --- /dev/null +++ b/src/main/java/io/github/onecx/workspace/domain/di/WorkspaceImportService.java @@ -0,0 +1,108 @@ +package io.github.onecx.workspace.domain.di; + +import java.util.LinkedList; +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + +import org.tkit.quarkus.context.ApplicationContext; +import org.tkit.quarkus.context.Context; + +import gen.io.github.onecx.workspace.di.workspace.v1.model.ImportRequestDTOV1; +import gen.io.github.onecx.workspace.di.workspace.v1.model.MenuItemStructureDTOV1; +import io.github.onecx.workspace.domain.daos.MenuItemDAO; +import io.github.onecx.workspace.domain.daos.ProductDAO; +import io.github.onecx.workspace.domain.daos.WorkspaceDAO; +import io.github.onecx.workspace.domain.di.mappers.WorkspaceDataImportMapperV1; +import io.github.onecx.workspace.domain.models.MenuItem; +import io.github.onecx.workspace.domain.models.Workspace; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ApplicationScoped +@Transactional(Transactional.TxType.NOT_SUPPORTED) +public class WorkspaceImportService { + + @Inject + WorkspaceDataImportMapperV1 mapper; + + @Inject + ProductDAO productDAO; + + @Inject + MenuItemDAO menuItemDAO; + + @Inject + WorkspaceDAO workspaceDAO; + + @Transactional(Transactional.TxType.REQUIRES_NEW) + public void deleteAll(String tenanId) { + try { + var ctx = Context.builder() + .principal("data-import") + .tenantId(tenanId) + .build(); + log.info("#### tenant to delete " + tenanId); + ApplicationContext.start(ctx); + + // clean data + productDAO.deleteAll(); + menuItemDAO.deleteAll(); + workspaceDAO.deleteAll(); + } finally { + ApplicationContext.close(); + } + } + + @Transactional(Transactional.TxType.REQUIRES_NEW) + public void importRequest(ImportRequestDTOV1 importRequestDTO) { + + try { + var ctx = Context.builder() + .principal("data-import") + .tenantId(importRequestDTO.getWorkspace().getTenantId()) + .build(); + + ApplicationContext.start(ctx); + + var dto = importRequestDTO.getWorkspace(); + var workspace = mapper.createWorkspace(dto); + + workspace = workspaceDAO.create(workspace); + + if (importRequestDTO.getMenuItems() != null && !importRequestDTO.getMenuItems().isEmpty()) { + menuItemDAO.deleteAllMenuItemsByWorkspaceId(workspace.getId()); + List menus = new LinkedList<>(); + recursiveMappingTreeStructure(importRequestDTO.getMenuItems(), workspace, null, menus); + menuItemDAO.create(menus); + } + } finally { + ApplicationContext.close(); + } + + } + + public void recursiveMappingTreeStructure(List items, Workspace workspace, MenuItem parent, + List mappedItems) { + int position = 0; + for (MenuItemStructureDTOV1 item : items) { + if (item != null) { + MenuItem menu = mapper.mapMenu(item); + menu.setWorkspace(workspace); + menu.setWorkspaceName(workspace.getWorkspaceName()); + menu.setPosition(position); + menu.setParent(parent); + mappedItems.add(menu); + position++; + + if (item.getChildren() == null || item.getChildren().isEmpty()) { + continue; + } + + recursiveMappingTreeStructure(item.getChildren(), workspace, menu, mappedItems); + } + } + } +} diff --git a/src/main/java/io/github/onecx/workspace/domain/di/mappers/WorkspaceDataImportMapperV1.java b/src/main/java/io/github/onecx/workspace/domain/di/mappers/WorkspaceDataImportMapperV1.java index 3eb41cd..a763a01 100644 --- a/src/main/java/io/github/onecx/workspace/domain/di/mappers/WorkspaceDataImportMapperV1.java +++ b/src/main/java/io/github/onecx/workspace/domain/di/mappers/WorkspaceDataImportMapperV1.java @@ -33,6 +33,7 @@ public abstract class WorkspaceDataImportMapperV1 { @Mapping(target = "controlTraceabilityManual", ignore = true) @Mapping(target = "modificationCount", ignore = true) @Mapping(target = "persisted", ignore = true) + @Mapping(target = "tenantId", ignore = true) public abstract Workspace createWorkspace(WorkspaceImportDTOV1 workspaceDTO); @Mapping(target = "id", ignore = true) @@ -44,6 +45,7 @@ public abstract class WorkspaceDataImportMapperV1 { @Mapping(target = "modificationCount", ignore = true) @Mapping(target = "persisted", ignore = true) @Mapping(target = "workspace", ignore = true) + @Mapping(target = "tenantId", ignore = true) public abstract Product createWorkspace(ProductDTOV1 productDTO); @Mapping(target = "id", ignore = true) @@ -72,6 +74,7 @@ public String map(List value) { @Mapping(target = "permission", ignore = true) @Mapping(target = "scope", ignore = true) @Mapping(target = "parent", ignore = true) + @Mapping(target = "tenantId", ignore = true) public abstract MenuItem mapMenu(MenuItemStructureDTOV1 menuItemStructureDto); } diff --git a/src/main/java/io/github/onecx/workspace/domain/models/MenuItem.java b/src/main/java/io/github/onecx/workspace/domain/models/MenuItem.java index 02b24fd..80b4c54 100644 --- a/src/main/java/io/github/onecx/workspace/domain/models/MenuItem.java +++ b/src/main/java/io/github/onecx/workspace/domain/models/MenuItem.java @@ -13,6 +13,7 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; +import org.hibernate.annotations.TenantId; import org.tkit.quarkus.jpa.models.TraceableEntity; import io.github.onecx.workspace.domain.models.enums.Scope; @@ -26,7 +27,7 @@ @Index(columnList = "ITEM_WORKSPACE", name = "WS_MENU_ITEM_ITEM_WORKSPACE_IDX"), @Index(columnList = "ITEM_PARENT", name = "WS_MENU_ITEM_ITEM_PARENT_IDX"), }, uniqueConstraints = { - @UniqueConstraint(name = "WS_MENU_ITEM_ITEM_KEY_WORKSPACE", columnNames = { "ITEM_KEY", "ITEM_WORKSPACE" }) + @UniqueConstraint(name = "WS_MENU_ITEM_ITEM_KEY_WORKSPACE", columnNames = { "ITEM_KEY", "ITEM_WORKSPACE", "TENANT_ID" }) }) @NamedEntityGraph(name = MenuItem.MENU_ITEM_WORKSPACE_AND_TRANSLATIONS, attributeNodes = { @NamedAttributeNode("i18n"), @NamedAttributeNode("workspace") }) @@ -37,6 +38,10 @@ public class MenuItem extends TraceableEntity { public static final String MENU_ITEM_WORKSPACE_AND_TRANSLATIONS = "MenuItem.workspaceAndTranslations"; + @TenantId + @Column(name = "TENANT_ID") + private String tenantId; + @ManyToOne(cascade = { REFRESH }, optional = false) @JoinColumn(name = "ITEM_WORKSPACE") Workspace workspace; diff --git a/src/main/java/io/github/onecx/workspace/domain/models/Microfrontend.java b/src/main/java/io/github/onecx/workspace/domain/models/Microfrontend.java index 4d0ba06..16b1ada 100644 --- a/src/main/java/io/github/onecx/workspace/domain/models/Microfrontend.java +++ b/src/main/java/io/github/onecx/workspace/domain/models/Microfrontend.java @@ -12,7 +12,7 @@ @Setter @Entity @Table(name = "MICROFRONTEND", uniqueConstraints = { - @UniqueConstraint(name = "MFE_ID_PATH_PRODUCT_GUID", columnNames = { "MFE_ID", "BASE_PATH", "PRODUCT_GUID" }) + @UniqueConstraint(name = "MFE_ID_PATH_PRODUCT_GUID", columnNames = { "BASE_PATH", "PRODUCT_GUID" }) }) @SuppressWarnings("squid:S2160") public class Microfrontend implements Serializable { diff --git a/src/main/java/io/github/onecx/workspace/domain/models/Product.java b/src/main/java/io/github/onecx/workspace/domain/models/Product.java index 095ea08..b236c0d 100644 --- a/src/main/java/io/github/onecx/workspace/domain/models/Product.java +++ b/src/main/java/io/github/onecx/workspace/domain/models/Product.java @@ -7,6 +7,7 @@ import jakarta.persistence.*; +import org.hibernate.annotations.TenantId; import org.tkit.quarkus.jpa.models.TraceableEntity; import lombok.Getter; @@ -16,11 +17,15 @@ @Setter @Entity @Table(name = "PRODUCT", uniqueConstraints = { - @UniqueConstraint(name = "PRODUCT_NAME_WORKSPACE_GUID", columnNames = { "PRODUCT_NAME", "WORKSPACE_GUID" }) + @UniqueConstraint(name = "PRODUCT_NAME_WORKSPACE_GUID", columnNames = { "PRODUCT_NAME", "WORKSPACE_GUID", "TENANT_ID" }) }) @SuppressWarnings("squid:S2160") public class Product extends TraceableEntity { + @TenantId + @Column(name = "TENANT_ID") + private String tenantId; + @Column(name = "PRODUCT_NAME") private String productName; diff --git a/src/main/java/io/github/onecx/workspace/domain/models/Workspace.java b/src/main/java/io/github/onecx/workspace/domain/models/Workspace.java index 770073a..90d0d71 100644 --- a/src/main/java/io/github/onecx/workspace/domain/models/Workspace.java +++ b/src/main/java/io/github/onecx/workspace/domain/models/Workspace.java @@ -8,6 +8,7 @@ import jakarta.persistence.*; +import org.hibernate.annotations.TenantId; import org.tkit.quarkus.jpa.models.TraceableEntity; import lombok.Getter; @@ -27,6 +28,7 @@ public class Workspace extends TraceableEntity { public static final String WORKSPACE_FULL = "Workspace.full"; + @TenantId @Column(name = "TENANT_ID") private String tenantId; diff --git a/src/main/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestController.java b/src/main/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestController.java index cd4ae0b..0691f68 100644 --- a/src/main/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestController.java +++ b/src/main/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestController.java @@ -3,7 +3,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; -import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import org.tkit.quarkus.log.cdi.LogService; @@ -14,7 +13,6 @@ @LogService @ApplicationScoped -@Path("/v1/workspaces/theme/{themeName}") @Transactional(Transactional.TxType.NOT_SUPPORTED) public class WorkspaceExternalV1RestController implements WorkspaceExternalV1Api { diff --git a/src/main/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestController.java b/src/main/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestController.java index 649d94e..8ccc4db 100644 --- a/src/main/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestController.java +++ b/src/main/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestController.java @@ -9,7 +9,6 @@ import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.validation.ConstraintViolationException; -import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; @@ -29,7 +28,6 @@ @LogService @ApplicationScoped -@Path("/internal/workspaces/{id}/menuItems") @Transactional(Transactional.TxType.NOT_SUPPORTED) public class MenuInternalRestController implements MenuInternalApi { @@ -49,6 +47,7 @@ public class MenuInternalRestController implements MenuInternalApi { WorkspaceDAO workspaceDAO; @Override + @Transactional public Response createMenuItemForWorkspace(String id, CreateMenuItemDTO menuItemDTO) { var workspace = workspaceDAO.findById(id); @@ -85,6 +84,7 @@ public Response createMenuItemForWorkspace(String id, CreateMenuItemDTO menuItem } @Override + @Transactional public Response deleteAllMenuItemsForWorkspace(String id) { dao.deleteAllMenuItemsByWorkspaceId(id); @@ -92,6 +92,7 @@ public Response deleteAllMenuItemsForWorkspace(String id) { } @Override + @Transactional public Response deleteMenuItemById(String id, String menuItemId) { dao.deleteQueryById(menuItemId); @@ -120,6 +121,7 @@ public Response getMenuStructureForWorkspaceId(String id) { } @Override + @Transactional public Response patchMenuItems(String id, List menuItemDTO) { // create map of Map tmp = menuItemDTO.stream() @@ -149,6 +151,7 @@ public Response patchMenuItems(String id, List menuItemDTO) { } @Override + @Transactional public Response updateMenuItem(String id, String menuItemId, MenuItemDTO menuItemDTO) { var menuItem = dao.findById(menuItemId); if (menuItem == null) { @@ -160,10 +163,13 @@ public Response updateMenuItem(String id, String menuItemId, MenuItemDTO menuIte mapper.update(menuItemDTO, menuItem); + menuItem = dao.update(menuItem); + return Response.ok(mapper.map(menuItem)).build(); } @Override + @Transactional public Response uploadMenuStructureForWorkspaceId(String id, WorkspaceMenuItemStructrueDTO menuItemStructrueDTO) { var workspace = workspaceDAO.findById(id); if (workspace == null) { diff --git a/src/main/java/io/github/onecx/workspace/rs/internal/controllers/ProductInternalRestController.java b/src/main/java/io/github/onecx/workspace/rs/internal/controllers/ProductInternalRestController.java index c5c714f..c5ab7cb 100644 --- a/src/main/java/io/github/onecx/workspace/rs/internal/controllers/ProductInternalRestController.java +++ b/src/main/java/io/github/onecx/workspace/rs/internal/controllers/ProductInternalRestController.java @@ -6,7 +6,6 @@ import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.validation.ConstraintViolationException; -import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; @@ -27,7 +26,6 @@ @LogService @ApplicationScoped -@Path("/internal/workspaces/{id}/products") @Transactional(Transactional.TxType.NOT_SUPPORTED) public class ProductInternalRestController implements ProductInternalApi { @@ -47,6 +45,7 @@ public class ProductInternalRestController implements ProductInternalApi { WorkspaceDAO workspaceDAO; @Override + @Transactional public Response createProductInWorkspace(String id, CreateProductRequestDTO createProductRequestDTO) { var workspace = workspaceDAO.findById(id); if (workspace == null) { @@ -64,6 +63,7 @@ public Response createProductInWorkspace(String id, CreateProductRequestDTO crea } @Override + @Transactional public Response deleteProductById(String id, String productId) { dao.deleteProduct(productId); @@ -77,6 +77,7 @@ public Response getProductsForWorkspaceId(String id) { } @Override + @Transactional public Response updateProductById(String id, String productId, UpdateProductRequestDTO updateProductRequestDTO) { var product = dao.findById(productId); if (product == null) { @@ -84,7 +85,7 @@ public Response updateProductById(String id, String productId, UpdateProductRequ } mapper.update(updateProductRequestDTO, product); - + product = dao.update(product); return Response.ok(mapper.map(product)).build(); } diff --git a/src/main/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestController.java b/src/main/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestController.java index d524e9d..ae47694 100644 --- a/src/main/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestController.java +++ b/src/main/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestController.java @@ -4,7 +4,6 @@ import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.validation.ConstraintViolationException; -import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; @@ -20,6 +19,7 @@ import gen.io.github.onecx.workspace.rs.internal.model.UpdateWorkspaceRequestDTO; import gen.io.github.onecx.workspace.rs.internal.model.WorkspaceSearchCriteriaDTO; import io.github.onecx.workspace.domain.daos.MenuItemDAO; +import io.github.onecx.workspace.domain.daos.ProductDAO; import io.github.onecx.workspace.domain.daos.WorkspaceDAO; import io.github.onecx.workspace.domain.models.Workspace; import io.github.onecx.workspace.rs.internal.mappers.InternalExceptionMapper; @@ -27,7 +27,6 @@ @LogService @ApplicationScoped -@Path("/internal/workspaces") @Transactional(Transactional.TxType.NOT_SUPPORTED) public class WorkspaceInternalRestController implements WorkspaceInternalApi { @@ -43,10 +42,14 @@ public class WorkspaceInternalRestController implements WorkspaceInternalApi { @Inject MenuItemDAO menuDao; + @Inject + ProductDAO productDAO; + @Context UriInfo uriInfo; @Override + @Transactional public Response createWorkspace(CreateWorkspaceRequestDTO createWorkspaceRequestDTO) { var workspace = workspaceMapper.create(createWorkspaceRequestDTO); workspace = dao.create(workspace); @@ -57,9 +60,11 @@ public Response createWorkspace(CreateWorkspaceRequestDTO createWorkspaceRequest } @Override + @Transactional public Response deleteWorkspace(String id) { // delete menu before deleting workspace menuDao.deleteAllMenuItemsByWorkspaceId(id); + productDAO.deleteProductByWorkspaceId(id); dao.deleteQueryById(id); return Response.noContent().build(); @@ -82,6 +87,7 @@ public Response searchWorkspace(WorkspaceSearchCriteriaDTO workspaceSearchCriter } @Override + @Transactional public Response updateWorkspace(String id, UpdateWorkspaceRequestDTO updateWorkspaceRequestDTO) { Workspace workspace = dao.findById(id); if (workspace == null) { diff --git a/src/main/java/io/github/onecx/workspace/rs/internal/mappers/MenuItemMapper.java b/src/main/java/io/github/onecx/workspace/rs/internal/mappers/MenuItemMapper.java index 86b74fb..c77a2db 100644 --- a/src/main/java/io/github/onecx/workspace/rs/internal/mappers/MenuItemMapper.java +++ b/src/main/java/io/github/onecx/workspace/rs/internal/mappers/MenuItemMapper.java @@ -32,6 +32,7 @@ public abstract class MenuItemMapper { @Mapping(target = "controlTraceabilityManual", ignore = true) @Mapping(target = "children", ignore = true) @Mapping(target = "applicationId", ignore = true) + @Mapping(target = "tenantId", ignore = true) public abstract MenuItem create(CreateMenuItemDTO dto); @Mapping(target = "workspace", ignore = true) @@ -48,6 +49,7 @@ public abstract class MenuItemMapper { @Mapping(target = "permission", ignore = true) @Mapping(target = "parent", ignore = true) @Mapping(target = "children", ignore = true) + @Mapping(target = "tenantId", ignore = true) public abstract void update(MenuItemDTO menuItemDetailsDto, @MappingTarget MenuItem entity); public abstract List mapList(List items); @@ -126,6 +128,7 @@ public void recursiveMappingTreeStructure(List items, Work @Mapping(target = "workspace", ignore = true) @Mapping(target = "permission", ignore = true) @Mapping(target = "scope", ignore = true) + @Mapping(target = "tenantId", ignore = true) public abstract MenuItem mapMenu(WorkspaceMenuItemDTO menuItemStructureDto); public void updateMenu(MenuItem menuItem, int position, Workspace workspace, diff --git a/src/main/java/io/github/onecx/workspace/rs/internal/mappers/ProductMapper.java b/src/main/java/io/github/onecx/workspace/rs/internal/mappers/ProductMapper.java index a3b7836..ca11661 100644 --- a/src/main/java/io/github/onecx/workspace/rs/internal/mappers/ProductMapper.java +++ b/src/main/java/io/github/onecx/workspace/rs/internal/mappers/ProductMapper.java @@ -1,5 +1,6 @@ package io.github.onecx.workspace.rs.internal.mappers; +import java.util.ArrayList; import java.util.List; import org.mapstruct.*; @@ -21,6 +22,7 @@ public interface ProductMapper { @Mapping(target = "creationUser", ignore = true) @Mapping(target = "creationDate", ignore = true) @Mapping(target = "controlTraceabilityManual", ignore = true) + @Mapping(target = "tenantId", ignore = true) Product create(CreateProductRequestDTO dto); @Mapping(target = "id", ignore = true) @@ -36,8 +38,23 @@ public interface ProductMapper { @Mapping(target = "creationUser", ignore = true) @Mapping(target = "creationDate", ignore = true) @Mapping(target = "controlTraceabilityManual", ignore = true) + @Mapping(target = "tenantId", ignore = true) + @Mapping(target = "microfrontends", qualifiedByName = "updateListDTO") void update(UpdateProductRequestDTO dto, @MappingTarget Product product); + @Named("updateListDTO") + default List updateMicrofrontendList(List listToUpdate) { + var list = new ArrayList(); + + if (listToUpdate != null) { + for (var mf : listToUpdate) { + list.add(update(mf)); + } + } + + return list; + } + @Mapping(target = "id", ignore = true) Microfrontend update(UpdateMicrofrontendDTO dto); diff --git a/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestController.java b/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestController.java index 05c8e1c..ada4966 100644 --- a/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestController.java +++ b/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestController.java @@ -3,7 +3,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; -import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import org.jboss.resteasy.reactive.RestResponse; @@ -20,7 +19,6 @@ @Slf4j @LogService @ApplicationScoped -@Path("/legacy/menustructure") @Transactional(Transactional.TxType.NOT_SUPPORTED) public class PortalLegacyRestController implements PortalLegacyApi { diff --git a/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitLegacyAppConfig.java b/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitLegacyAppConfig.java new file mode 100644 index 0000000..a5b5bda --- /dev/null +++ b/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitLegacyAppConfig.java @@ -0,0 +1,11 @@ +package io.github.onecx.workspace.rs.legacy.controllers; + +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigMapping(prefix = "tkit.legacy") +public interface TkitLegacyAppConfig { + + @WithDefault("false") + boolean enableMenuAutoRegistration(); +} diff --git a/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestController.java b/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestController.java index 362bdc1..1f528e2 100644 --- a/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestController.java +++ b/src/main/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestController.java @@ -6,7 +6,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; -import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; @@ -31,13 +30,15 @@ @Slf4j @LogService @ApplicationScoped -@Path("/1000kit-portal-server/menustructure/{portalName}") @Transactional(Transactional.TxType.NOT_SUPPORTED) public class TkitPortalRestController implements TkitPortalApi { @ConfigProperty(name = "tkit.legacy.enable.menu.auto.registration", defaultValue = "false") boolean enableAutoRegistration; + @Inject + TkitLegacyAppConfig appConfig; + @Inject MenuItemDAO menuItemDAO; @@ -63,12 +64,13 @@ public Response getMenuStructureForTkitPortalName(String portalName, Boolean int } @Override + @Transactional public Response submitMenuRegistrationRequest(String portalName, String appId, MenuRegistrationRequestDTO menuRegistrationRequestDTO) { MenuRegistrationResponseDTO response = new MenuRegistrationResponseDTO(); response.setApplicationId(appId); response.setRequestVersion(menuRegistrationRequestDTO.getRequestVersion()); - if (!enableAutoRegistration) { + if (!appConfig.enableMenuAutoRegistration()) { log.info("Auto registration of menu requests is disabled, ignoring request from {}", appId); response.setApplied(false); response.setNotice("TKITPORTAL10003 Menu registration request has been ignored"); diff --git a/src/main/java/io/github/onecx/workspace/rs/legacy/mappers/TkitPortalMapper.java b/src/main/java/io/github/onecx/workspace/rs/legacy/mappers/TkitPortalMapper.java index 8437607..845fde4 100644 --- a/src/main/java/io/github/onecx/workspace/rs/legacy/mappers/TkitPortalMapper.java +++ b/src/main/java/io/github/onecx/workspace/rs/legacy/mappers/TkitPortalMapper.java @@ -54,6 +54,7 @@ default void recursiveMappingTreeStructure(List items, @Mapping(target = "persisted", ignore = true) @Mapping(target = "workspace", ignore = true) @Mapping(target = "permission", source = "permissionObject") + @Mapping(target = "tenantId", ignore = true) MenuItem mapMenu(TkitMenuItemStructureDTO menuItemStructureDto); default void updateMenu(MenuItem menuItem, int position, Workspace workspace, diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 2594ee5..fdb9dd1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,6 +5,7 @@ quarkus.datasource.jdbc.min-size=10 quarkus.banner.enabled=false quarkus.hibernate-orm.database.generation=validate +quarkus.hibernate-orm.multitenant=DISCRIMINATOR #quarkus.flyway.migrate-at-start=true #quarkus.flyway.validate-on-migrate=true quarkus.liquibase.migrate-at-start=true @@ -17,12 +18,44 @@ tkit.dataimport.configurations.workspace.metadata.operation=CLEAN_INSERT tkit.dataimport.configurations.workspace.enabled=false tkit.dataimport.configurations.workspace.stop-at-error=true +# Disable the menu auto registration for old legacy applications +tkit.legacy.enable-menu-auto-registration=false + +# enable or disable multi-tenancy support +tkit.rs.context.tenant-id.enabled=true # DEV %dev.tkit.log.json.enabled=false # TEST %test.tkit.log.json.enabled=false +%test.tkit.dataimport.enabled=true +%test.tkit.dataimport.configurations.workspace.enabled=true +%test.tkit.dataimport.configurations.workspace.file=./src/test/resources/import/workspace-import.json +%test.tkit.dataimport.configurations.workspace.metadata.operation=CLEAN_INSERT +%test.tkit.dataimport.configurations.workspace.stop-at-error=true +# Enable mocking for tenant service +%test.tkit.jpa.tenant.default=tenant-100 +%test.tkit.rs.context.tenant-id.mock.enabled=true +%test.tkit.rs.context.tenant-id.mock.default-tenant=tenant-100 +%test.tkit.rs.context.tenant-id.mock.claim-org-id=orgId +%test.tkit.rs.context.tenant-id.mock.token-header-param=apm-principal-token +%test.tkit.rs.context.tenant-id.mock.data.org1=tenant-100 +%test.tkit.rs.context.tenant-id.mock.data.org2=tenant-200 +%test.tkit.rs.context.tenant-id.mock.data.org3=tenant-300 + +# TEST-IT (integration tests) +quarkus.test.integration-test-profile=test-it +%test-it.tkit.log.json.enabled=false +%test-it.tkit.jpa.tenant.default=tenant-100 +%test-it.tkit.rs.context.tenant-id.mock.enabled=true +%test-it.tkit.rs.context.tenant-id.mock.default-tenant=tenant-100 +%test-it.tkit.rs.context.tenant-id.mock.claim-org-id=orgId +%test-it.tkit.rs.context.tenant-id.mock.token-header-param=apm-principal-token +%test-it.tkit.rs.context.tenant-id.mock.data.org1=tenant-100 +%test-it.tkit.rs.context.tenant-id.mock.data.org2=tenant-200 +%test-it.tkit.rs.context.tenant-id.mock.data.org3=tenant-300 + # PROD %prod.quarkus.datasource.jdbc.url=${DB_URL:jdbc:postgresql://postgresdb:5432/onecx-workspace?sslmode=disable} diff --git a/src/main/resources/db/v1/2023-11-09-create-tables.xml b/src/main/resources/db/v1/2023-11-09-create-tables.xml index 5e06e40..2541745 100644 --- a/src/main/resources/db/v1/2023-11-09-create-tables.xml +++ b/src/main/resources/db/v1/2023-11-09-create-tables.xml @@ -13,6 +13,9 @@ + + + @@ -60,6 +63,9 @@ + + + @@ -83,7 +89,9 @@ - + + + @@ -106,13 +114,13 @@ - + - + - + diff --git a/src/test/java/io/github/onecx/workspace/AfterStartDataImportTest.java b/src/test/java/io/github/onecx/workspace/AfterStartDataImportTest.java new file mode 100644 index 0000000..47870f0 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/AfterStartDataImportTest.java @@ -0,0 +1,32 @@ +package io.github.onecx.workspace; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; + +import io.github.onecx.workspace.domain.daos.WorkspaceDAO; +import io.github.onecx.workspace.domain.models.Workspace; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class AfterStartDataImportTest extends AbstractTest { + + @Inject + WorkspaceDAO workspaceDAO; + + @Test + void importDataFromFileTest() { + Stream result = workspaceDAO.findAll(); + var restulList = result.toList(); + assertThat(restulList).hasSize(1); + assertThat(restulList.get(0).getWorkspaceName()).isEqualTo("ADMIN"); + assertThat(restulList.get(0).getTenantId()).isEqualTo("tenant-100"); + + } + +} diff --git a/src/test/java/io/github/onecx/workspace/domain/daos/MenuItemDAOTest.java b/src/test/java/io/github/onecx/workspace/domain/daos/MenuItemDAOTest.java index 95bd1aa..5c2eb45 100644 --- a/src/test/java/io/github/onecx/workspace/domain/daos/MenuItemDAOTest.java +++ b/src/test/java/io/github/onecx/workspace/domain/daos/MenuItemDAOTest.java @@ -39,6 +39,8 @@ void methodExceptionTests() { MenuItemDAO.ErrorKeys.ERROR_DELETE_ALL_MENU_ITEMS_BY_WORKSPACE_NAME_AND_APP_ID); methodExceptionTests(() -> dao.loadMenuItemByWorkspaceAndKey(null, null), MenuItemDAO.ErrorKeys.ERROR_LOAD_ALL_MENU_ITEMS_BY_WORKSPACE_NAME); + methodExceptionTests(() -> dao.findById(null), + MenuItemDAO.ErrorKeys.FIND_ENTITY_BY_ID_FAILED); } void methodExceptionTests(Executable fn, Enum key) { diff --git a/src/test/java/io/github/onecx/workspace/domain/daos/ProductDAOTest.java b/src/test/java/io/github/onecx/workspace/domain/daos/ProductDAOTest.java index b0c6330..4cebdac 100644 --- a/src/test/java/io/github/onecx/workspace/domain/daos/ProductDAOTest.java +++ b/src/test/java/io/github/onecx/workspace/domain/daos/ProductDAOTest.java @@ -30,6 +30,10 @@ void beforeAll() { void methodExceptionTests() { methodExceptionTests(() -> dao.getProductsForWorkspaceId(null), ProductDAO.ErrorKeys.ERROR_FIND_PRODUCTS_BY_WORKSPACE_ID); + methodExceptionTests(() -> dao.findById(null), + ProductDAO.ErrorKeys.FIND_ENTITY_BY_ID_FAILED); + methodExceptionTests(() -> dao.deleteProduct(null), + ProductDAO.ErrorKeys.ERROR_DELETE_PRODUCT_ID); } void methodExceptionTests(Executable fn, Enum key) { diff --git a/src/test/java/io/github/onecx/workspace/domain/daos/WorkspaceDAOTest.java b/src/test/java/io/github/onecx/workspace/domain/daos/WorkspaceDAOTest.java index 9d803d9..fd8b285 100644 --- a/src/test/java/io/github/onecx/workspace/domain/daos/WorkspaceDAOTest.java +++ b/src/test/java/io/github/onecx/workspace/domain/daos/WorkspaceDAOTest.java @@ -36,6 +36,8 @@ void methodExceptionTests() { WorkspaceDAO.ErrorKeys.ERROR_FIND_WORKSPACE_NAME); methodExceptionTests(() -> dao.findByThemeName(null), WorkspaceDAO.ErrorKeys.ERROR_FIND_BY_THEME_NAME); + methodExceptionTests(() -> dao.findById(null), + WorkspaceDAO.ErrorKeys.FIND_ENTITY_BY_ID_FAILED); } void methodExceptionTests(Executable fn, Enum key) { diff --git a/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceExceptionTest.java b/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceExceptionTest.java index ddfaf1a..c605334 100644 --- a/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceExceptionTest.java +++ b/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceExceptionTest.java @@ -62,6 +62,7 @@ public byte[] getData() { importRequest.setWorkspace(workspace); workspace.setWorkspaceName("test1"); workspace.setBaseUrl("baseurl"); + workspace.setTenantId("tenant-100"); return mapper.writeValueAsBytes(data); } catch (Exception ex) { throw new RuntimeException(ex); @@ -85,6 +86,7 @@ public byte[] getData() { ImportRequestDTOV1 importRequest = new ImportRequestDTOV1(); data.getRequests().add(importRequest); WorkspaceImportDTOV1 workspace = new WorkspaceImportDTOV1(); + workspace.setTenantId("tenant-100"); importRequest.setWorkspace(workspace); return mapper.writeValueAsBytes(data); } catch (Exception ex) { @@ -116,5 +118,8 @@ public byte[] getData() { }; assertThrows(WorkspaceDataImportService.ImportException.class, () -> service.importData(config3)); + + doThrow(RuntimeException.class).when(dao).deleteAll(); + assertThrows(WorkspaceDataImportService.ImportException.class, () -> service.importData(config)); } } diff --git a/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceFileTest.java b/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceFileTest.java deleted file mode 100644 index 852837c..0000000 --- a/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceFileTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.onecx.workspace.domain.di; - -import java.util.Map; -import java.util.stream.Stream; - -import jakarta.inject.Inject; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import io.github.onecx.workspace.domain.daos.WorkspaceDAO; -import io.github.onecx.workspace.domain.models.Workspace; -import io.github.onecx.workspace.test.AbstractTest; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.QuarkusTestProfile; -import io.quarkus.test.junit.TestProfile; - -@QuarkusTest -@DisplayName("Portal data import test from example file") -@TestProfile(WorkspaceDataImportServiceFileTest.CustomProfile.class) -class WorkspaceDataImportServiceFileTest extends AbstractTest { - - @Inject - WorkspaceDAO workspaceDAO; - - @Test - @DisplayName("Import portal data from file") - void importDataFromFileTest() { - Stream result = workspaceDAO.findAll(); - Assertions.assertEquals(1, result.count()); - - } - - public static class CustomProfile implements QuarkusTestProfile { - - @Override - public String getConfigProfile() { - return "test"; - } - - @Override - public Map getConfigOverrides() { - return Map.of( - "tkit.dataimport.enabled", "true", - "tkit.dataimport.configurations.workspace.enabled", "true", - "tkit.dataimport.configurations.workspace.file", "./src/test/resources/import/workspace-import.json", - "tkit.dataimport.configurations.workspace.metadata.operation", "CLEAN_INSERT", - "tkit.dataimport.configurations.workspace.stop-at-error", "true"); - } - } - -} diff --git a/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceTest.java b/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceTest.java index 0497af8..07c3429 100644 --- a/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceTest.java +++ b/src/test/java/io/github/onecx/workspace/domain/di/WorkspaceDataImportServiceTest.java @@ -50,11 +50,11 @@ void importDataTest() { Assertions.assertNull(workspace); Stream result = dao.findAll(); - Assertions.assertEquals(3, result.count()); + Assertions.assertEquals(2, result.count()); } @Test - void importDataWithoutMenuItemsTest() { + void importDataMenuItemsNullTest() { service.importData(new DataImportConfig() { @Override public Map getMetadata() { @@ -72,6 +72,7 @@ public byte[] getData() { importRequest.setWorkspace(workspace); workspace.setWorkspaceName("test1"); workspace.setBaseUrl("baseurl"); + workspace.setTenantId("tenant-100"); importRequest.setMenuItems(null); return mapper.writeValueAsBytes(data); } catch (Exception ex) { @@ -82,7 +83,10 @@ public byte[] getData() { var workspaces = dao.findAll().toList(); assertThat(workspaces).hasSize(1); + } + @Test + void importDataMenuItemsEmptyTest() { service.importData(new DataImportConfig() { @Override public Map getMetadata() { @@ -99,6 +103,7 @@ public byte[] getData() { WorkspaceImportDTOV1 workspace = new WorkspaceImportDTOV1(); importRequest.setWorkspace(workspace); workspace.setWorkspaceName("test1"); + workspace.setTenantId("tenant-100"); workspace.setBaseUrl("baseurl"); importRequest.setMenuItems(new ArrayList<>()); return mapper.writeValueAsBytes(data); @@ -108,7 +113,7 @@ public byte[] getData() { } }); - workspaces = dao.findAll().toList(); + var workspaces = dao.findAll().toList(); assertThat(workspaces).hasSize(1); } @@ -122,7 +127,7 @@ public Map getMetadata() { }); var workspaces = dao.findAll().toList(); - assertThat(workspaces).hasSize(3); + assertThat(workspaces).hasSize(2); } @Test @@ -135,7 +140,7 @@ public Map getMetadata() { }); var workspaces = dao.findAll().toList(); - assertThat(workspaces).hasSize(3); + assertThat(workspaces).hasSize(2); } @Test diff --git a/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerIT.java b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerIT.java index bd85471..ebd7397 100644 --- a/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerIT.java +++ b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerIT.java @@ -1,8 +1,7 @@ package io.github.onecx.workspace.rs.external.v1.controllers; -import io.github.onecx.workspace.test.AbstractTest; import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest -public class WorkspaceExternalV1RestControllerIT extends AbstractTest { +public class WorkspaceExternalV1RestControllerIT extends WorkspaceExternalV1RestControllerTest { } diff --git a/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantIT.java b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantIT.java new file mode 100644 index 0000000..dfc8b40 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantIT.java @@ -0,0 +1,7 @@ +package io.github.onecx.workspace.rs.external.v1.controllers; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class WorkspaceExternalV1RestControllerTenantIT extends WorkspaceExternalV1RestControllerTenantTest { +} diff --git a/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantTest.java new file mode 100644 index 0000000..e482923 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTenantTest.java @@ -0,0 +1,51 @@ +package io.github.onecx.workspace.rs.external.v1.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.OK; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.external.v1.model.WorkspaceInfoListDTOV1; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@TestHTTPEndpoint(WorkspaceExternalV1RestController.class) +@WithDBData(value = "data/testdata-external.xml", deleteBeforeInsert = true, deleteAfterTest = true, rinseAndRepeat = true) +class WorkspaceExternalV1RestControllerTenantTest extends AbstractTest { + + @ParameterizedTest + @MethodSource("themeNamesAndResults") + void getWorkspacesByThemeName(String themeName, int results, String organisation) { + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("themeName", themeName) + .header(APM_HEADER_PARAM, createToken(organisation)) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(WorkspaceInfoListDTOV1.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getWorkspaces()).hasSize(results); + } + + private static Stream themeNamesAndResults() { + return Stream.of( + arguments("11-111", 3, "org1"), + arguments("22-222", 0, "org1"), // different tenant so will not find it + arguments("does-not-exists", 0, "org1"), + arguments("11-111", 0, "org2"), + arguments("22-222", 2, "org2")); + } +} diff --git a/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTest.java b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTest.java index 4d5c252..c2f80c3 100644 --- a/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTest.java +++ b/src/test/java/io/github/onecx/workspace/rs/external/v1/controllers/WorkspaceExternalV1RestControllerTest.java @@ -42,7 +42,7 @@ void getWorkspacesByThemeName(String themeName, int results) { private static Stream themeNamesAndResults() { return Stream.of( arguments("11-111", 3), - arguments("22-222", 2), + arguments("22-222", 0), // different tenant so will not find it arguments("does-not-exists", 0)); } } diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantIT.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantIT.java new file mode 100644 index 0000000..bbd64cb --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.workspace.rs.internal.controllers; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class MenuInternalRestControllerTenantIT extends MenuInternalRestControllerTenantTest { + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantTest.java new file mode 100644 index 0000000..e41dab7 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTenantTest.java @@ -0,0 +1,450 @@ +package io.github.onecx.workspace.rs.internal.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.*; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import jakarta.ws.rs.core.HttpHeaders; + +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.internal.model.*; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.common.mapper.TypeRef; +import io.restassured.mapper.ObjectMapperType; + +@QuarkusTest +@TestHTTPEndpoint(MenuInternalRestController.class) +@WithDBData(value = "data/testdata-internal.xml", deleteBeforeInsert = true, deleteAfterTest = true, rinseAndRepeat = true) +class MenuInternalRestControllerTenantTest extends AbstractTest { + + @Test + void getMenuItemsForWorkspaceIdTest() { + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(List.class); + + assertThat(dto).isNotNull().isNotEmpty().hasSize(13); + + dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org3")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(List.class); + + assertThat(dto).isNotNull().isEmpty(); + } + + @Test + void deleteMenuItemByIdTest() { + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("menuItemId", "33-13") + .header(APM_HEADER_PARAM, createToken("org3")) + .delete("{menuItemId}") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(List.class); + + assertThat(dto).isNotNull().isNotEmpty().hasSize(13); + + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("menuItemId", "33-13") + .header(APM_HEADER_PARAM, createToken("org1")) + .delete("{menuItemId}") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(List.class); + + assertThat(dto).isNotNull().isNotEmpty().hasSize(12); + } + + @Test + void deleteAllMenuItemsForWorkspaceTest() { + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org2")) + .delete() + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(List.class); + + assertThat(dto).isNotNull().isNotEmpty(); + + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .delete() + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(List.class); + + assertThat(dto).isNotNull().isEmpty(); + } + + @Test + void createMenuItemForWorkspaceTest() { + CreateMenuItemDTO menuItem = new CreateMenuItemDTO(); + menuItem.setName("menu"); + menuItem.setKey("test01_menu"); + menuItem.setDisabled(false); + menuItem.setRoles(List.of("Role1")); + menuItem.setParentItemId("44-1"); + menuItem.setI18n(Map.of("de", "Test DE Menu", "en", "Test EN Menu")); + + var error = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org3")) + .body(menuItem) + .post() + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(error).isNotNull(); + assertThat(error.getErrorCode()).isEqualTo("WORKSPACE_DOES_NOT_EXIST"); + + var uri = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .body(menuItem) + .post() + .then() + .statusCode(CREATED.getStatusCode()) + .extract().header(HttpHeaders.LOCATION); + + assertThat(uri).isNotNull(); + + var dto = given().when() + .contentType(APPLICATION_JSON) + .header(APM_HEADER_PARAM, createToken("org1")) + .get(uri) + .then().statusCode(OK.getStatusCode()) + .extract().as(MenuItemDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getName()).isEqualTo(menuItem.getName()); + assertThat(dto.getDescription()).isEqualTo(menuItem.getDescription()); + } + + @Test + void getMenuItemByIdTest() { + var dto = given().when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("menuItemId", "33-6") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{menuItemId}") + .then().statusCode(OK.getStatusCode()) + .extract().as(MenuItemDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getName()).isEqualTo("Portal Child 1"); + + dto = given().when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("menuItemId", "33-6") + .header(APM_HEADER_PARAM, createToken("org2")) + .get("{menuItemId}") + .then().statusCode(OK.getStatusCode()) + .extract().as(MenuItemDTO.class, ObjectMapperType.GSON); + assertThat(dto).isNull(); + } + + @Test + void getMenuStructureForWorkspaceIdTest() { + var data = given() + .when() + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org2")) + .get("/tree") + .then() + .statusCode(OK.getStatusCode()) + .extract().body().as(WorkspaceMenuItemStructrueDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getMenuItems()).isEmpty(); + + data = given() + .when() + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("/tree") + .then() + .statusCode(OK.getStatusCode()) + .extract().body().as(WorkspaceMenuItemStructrueDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getMenuItems()).hasSize(5); + assertThat(countMenuItems(data.getMenuItems())).isEqualTo(13); + } + + private int countMenuItems(Collection menuItemDTOS) { + int count = 0; + for (WorkspaceMenuItemDTO item : menuItemDTOS) { + count++; + if (item.getChildren() != null && !item.getChildren().isEmpty()) { + count += countMenuItems(item.getChildren()); + } + } + + return count; + } + + @Test + void bulkPatchMenuItemsEmptyListTest() { + given() + .when() + .contentType(APPLICATION_JSON) + .body(List.of()) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org3")) + .patch() + .then() + .statusCode(NOT_FOUND.getStatusCode()); + } + + @Test + void patchMenuItemsTest() { + var newRolesMenuItem = List.of("Role2"); + + var menuItemDetailsDTO = new MenuItemDTO(); + menuItemDetailsDTO.setId("44-1"); + menuItemDetailsDTO.setName("Test menu 44-1"); + menuItemDetailsDTO.setDisabled(false); + menuItemDetailsDTO.setRoles(newRolesMenuItem); + + var menuItemDetailsDTO1 = new MenuItemDTO(); + menuItemDetailsDTO1.setId("44-2"); + menuItemDetailsDTO1.setParentItemId("44-5"); + menuItemDetailsDTO1.setName("Test menu 44-2"); + menuItemDetailsDTO1.setDisabled(false); + menuItemDetailsDTO1.setRoles(newRolesMenuItem); + + given() + .when() + .contentType(APPLICATION_JSON) + .body(List.of(menuItemDetailsDTO, menuItemDetailsDTO1)) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org3")) + .patch() + .then().statusCode(NOT_FOUND.getStatusCode()); + + var updatedData = given() + .when() + .contentType(APPLICATION_JSON) + .body(List.of(menuItemDetailsDTO, menuItemDetailsDTO1)) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .patch() + .then().statusCode(OK.getStatusCode()) + .extract() + .as(new TypeRef>() { + }); + + assertThat(updatedData).isNotNull().hasSize(2); + + updatedData.forEach(e -> { + switch (e.getName()) { + case "Test menu 44-1": + case "Test menu 44-2": + assertThat(e.getWorkspaceName()).isEqualTo("test02"); + break; + default: + assertThat(e.getWorkspaceName()).isNull(); + } + }); + } + + @Test + void updateMenuItemTest() { + var request = new MenuItemDTO(); + request.setKey("Test menu"); + request.setDescription("New test menu description"); + request.setDisabled(false); + request.setParentItemId("44-1"); + + given() + .when() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("id", "11-222") + .pathParam("menuItemId", "44-6") + .header(APM_HEADER_PARAM, createToken("org3")) + .put("{menuItemId}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + // update menu item + var updatedData = given() + .when() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("id", "11-222") + .pathParam("menuItemId", "44-6") + .header(APM_HEADER_PARAM, createToken("org1")) + .put("{menuItemId}") + .then() + .statusCode(OK.getStatusCode()) + .extract().as(MenuItemDTO.class); + + assertThat(updatedData).isNotNull(); + assertThat(updatedData.getKey()).isEqualTo(request.getKey()); + assertThat(updatedData.getDescription()).isEqualTo(request.getDescription()); + assertThat(updatedData.getWorkspaceName()).isEqualTo("test02"); + + var dto = given().when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-222") + .pathParam("menuItemId", "44-6") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{menuItemId}") + .then() + .statusCode(OK.getStatusCode()) + .extract().as(MenuItemDTO.class); + + assertThat(dto).isNotNull(); + assertThat(updatedData.getKey()).isEqualTo(request.getKey()); + assertThat(updatedData.getWorkspaceName()).isEqualTo("test02"); + assertThat(updatedData.getDescription()).isEqualTo(request.getDescription()); + assertThat(dto.getName()).isEqualTo("Portal Child 1"); + } + + @Test + void uploadMenuStructureTest() { + + var menuStructureListDTO = new WorkspaceMenuItemStructrueDTO(); + var menuItemStructureDTO = new WorkspaceMenuItemDTO(); + + menuItemStructureDTO.setKey("Test menu"); + menuItemStructureDTO.setDisabled(false); + menuItemStructureDTO.setParentItemId("44-1"); + menuItemStructureDTO.setChildren(new ArrayList<>()); + + var menuItemStructureDTO1 = new WorkspaceMenuItemDTO(); + menuItemStructureDTO1.setKey("Sub menu"); + menuItemStructureDTO1.setDisabled(false); + menuItemStructureDTO1.setParentItemId("44-1"); + menuItemStructureDTO.addChildrenItem(menuItemStructureDTO1); + menuItemStructureDTO1 = new WorkspaceMenuItemDTO(); + menuItemStructureDTO1.setKey("Sub menu2"); + menuItemStructureDTO1.setDisabled(false); + menuItemStructureDTO1.setParentItemId("44-1"); + menuItemStructureDTO.addChildrenItem(menuItemStructureDTO1); + menuItemStructureDTO1 = new WorkspaceMenuItemDTO(); + menuItemStructureDTO1.setKey("Sub menu3"); + menuItemStructureDTO1.setDisabled(false); + menuItemStructureDTO1.setParentItemId("44-1"); + menuItemStructureDTO.addChildrenItem(menuItemStructureDTO1); + + menuStructureListDTO.addMenuItemsItem(menuItemStructureDTO); + + //update menu structure with another tenant + var error = given() + .when() + .contentType(APPLICATION_JSON) + .body(menuStructureListDTO) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org2")) + .post("/tree/upload") + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(error).isNotNull(); + assertThat(error.getErrorCode()).isEqualTo("WORKSPACE_DOES_NOT_EXIST"); + + // update menu item + given() + .when() + .contentType(APPLICATION_JSON) + .body(menuStructureListDTO) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .post("/tree/upload") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + var data = given() + .when() + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("/tree") + .then() + .statusCode(OK.getStatusCode()) + .extract().body().as(WorkspaceMenuItemStructrueDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getMenuItems()).hasSize(1); + assertThat(countMenuItems(data.getMenuItems())).isEqualTo(4); + } +} diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTest.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTest.java index 48640f3..adb3149 100644 --- a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTest.java +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/MenuInternalRestControllerTest.java @@ -373,11 +373,35 @@ void patchMenuItemDoesNotUpdateParentTest() { assertThat(updatedData.getWorkspaceName()).isEqualTo("test02"); } + @Test + void patchMenuItemParentSetChildAsParentTest() { + + var request = new MenuItemDTO(); + request.setKey("Test menu"); + request.setDisabled(false); + request.setParentItemId("44-6"); + + // update menu item + var error = given() + .when() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("id", "11-222") + .pathParam("menuItemId", "44-2") + .put("{menuItemId}") + .then().statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(error).isNotNull(); + assertThat(error.getErrorCode()).isEqualTo("CYCLE_DEPENDENCY"); + } + static Stream inputParams() { return Stream.of( - Arguments.of("22-111", "55-4", "55-6"), + Arguments.of("11-111", "33-2", "55-6"), Arguments.of("11-222", "44-6", "does-not-exists"), - Arguments.of("11-222", "44-6", "55-1")); + Arguments.of("11-222", "44-6", "55-1"), + Arguments.of("11-222", "44-6", "33-11")); } @ParameterizedTest diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantIT.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantIT.java new file mode 100644 index 0000000..41c1688 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.workspace.rs.internal.controllers; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class ProductRestControllerTenantIT extends ProductRestControllerTenantTest { + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantTest.java new file mode 100644 index 0000000..65eb62d --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTenantTest.java @@ -0,0 +1,314 @@ +package io.github.onecx.workspace.rs.internal.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.*; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.internal.model.*; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +@TestHTTPEndpoint(ProductInternalRestController.class) +@WithDBData(value = "data/testdata-internal.xml", deleteBeforeInsert = true, deleteAfterTest = true, rinseAndRepeat = true) +class ProductRestControllerTenantTest extends AbstractTest { + + @Test + void createProductInWorkspaceTest() { + var request = new CreateProductRequestDTO(); + request.setProductName("testProduct"); + request.setBaseUrl("/test"); + var mfe = new CreateMicrofrontendDTO(); + mfe.setMfeId("testMfe1"); + mfe.setBasePath("/testMfe1"); + request.addMicrofrontendsItem(mfe); + mfe = new CreateMicrofrontendDTO(); + mfe.setMfeId("testMfe2"); + mfe.setBasePath("/testMfe2"); + request.addMicrofrontendsItem(mfe); + + // test workspace under different tenant + var error = given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org2")) + .post() + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(error).isNotNull(); + assertThat(error.getErrorCode()).isEqualTo("WORKSPACE_DOES_NOT_EXIST"); + + var dto = given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .post() + .then() + .statusCode(CREATED.getStatusCode()) + .extract().as(ProductDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getMicrofrontends()).hasSize(2); + + request.setMicrofrontends(null); + request.setProductName("testProduct1"); + request.setBaseUrl("/test1"); + dto = given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .post() + .then() + .statusCode(CREATED.getStatusCode()) + .extract().as(ProductDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getMicrofrontends()).isNull(); + + // test the same with org 3 the workspace will not be found + error = given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org3")) + .post() + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(error).isNotNull(); + assertThat(error.getErrorCode()).isEqualTo("WORKSPACE_DOES_NOT_EXIST"); + } + + @Test + void deleteProductByIdTest() { + // delete with different tenant + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org2")) + .pathParam("productId", "5678") + .delete("{productId}") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(dto).isNotNull().isNotEmpty().hasSize(2); + assertThat(dto.get(0).getMicrofrontends()).isNotEmpty(); + + // delete with correct tenant + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .pathParam("productId", "5678") + .delete("{productId}") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(dto).isNotNull().isNotEmpty().hasSize(1); + assertThat(dto.get(0).getMicrofrontends()).isNotEmpty(); + } + + @Test + void getProductsForWorkspaceIdTest() { + // existing product different tenant + var response = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org3")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(response).isNotNull().isEmpty(); + + // existing product + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(dto).isNotNull().isNotEmpty().hasSize(2); + assertThat(dto.get(0).getMicrofrontends()).isNotEmpty(); + assertThat(dto.get(1).getMicrofrontends()).isNotEmpty(); + } + + @Test + void updateProductByIdTest() { + var request = new UpdateProductRequestDTO(); + request.setBaseUrl("/onecx-core"); + + // not sending request + var error = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("productId", "1234") + .header(APM_HEADER_PARAM, createToken("org2")) + .put("{productId}") + .then() + .statusCode(BAD_REQUEST.getStatusCode()); + + assertThat(error).isNotNull(); + + // not existing product + given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("productId", "1234") + .header(APM_HEADER_PARAM, createToken("org2")) + .put("{productId}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + var productDTOList = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + var product = productDTOList.get(0); + request.setBaseUrl("/mho-test"); + request.setMicrofrontends(new ArrayList<>()); + for (var mf : product.getMicrofrontends()) { + var updateDto = new UpdateMicrofrontendDTO(); + updateDto.setBasePath(mf.getBasePath()); + updateDto.setMfeId(mf.getMfeId()); + request.getMicrofrontends().add(updateDto); + } + request.getMicrofrontends().get(0).setBasePath("/mfe1-test"); + + // do not find as another tenant + given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("productId", "1234") + .header(APM_HEADER_PARAM, createToken("org2")) + .put("{productId}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + var dto = given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("productId", "1234") + .header(APM_HEADER_PARAM, createToken("org1")) + .put("{productId}") + .then() + .statusCode(OK.getStatusCode()) + .extract().as(ProductDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getMicrofrontends()).isNotEmpty(); + assertThat(dto.getBaseUrl()).isEqualTo(request.getBaseUrl()); + + var emptyResponse = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org3")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(emptyResponse).isNotNull().isEmpty(); + + var response = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(response).isNotNull().isNotEmpty().hasSize(2); + var filteredProduct = response.stream().filter(x -> x.getProductName().equals(product.getProductName())).findFirst(); + assertThat(filteredProduct.get().getMicrofrontends()).isNotEmpty(); + assertThat(filteredProduct.get().getMicrofrontends().get(0).getBasePath()) + .isEqualTo(request.getMicrofrontends().get(0).getBasePath()); + + request.setMicrofrontends(new ArrayList<>()); + dto = given() + .when() + .body(request) + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("productId", "1234") + .header(APM_HEADER_PARAM, createToken("org1")) + .put("{productId}") + .then() + .statusCode(OK.getStatusCode()) + .extract().as(ProductDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getMicrofrontends()).isEmpty(); + assertThat(dto.getBaseUrl()).isEqualTo(request.getBaseUrl()); + } +} diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTest.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTest.java index 67ff030..2a47c9f 100644 --- a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTest.java +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/ProductRestControllerTest.java @@ -91,6 +91,15 @@ void deleteProductByIdTest() { .then() .statusCode(NO_CONTENT.getStatusCode()); + given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .pathParam("productId", "5678") + .delete("{productId}") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + var dto = given() .when() .contentType(APPLICATION_JSON) @@ -164,6 +173,27 @@ void updateProductByIdTest() { .then() .statusCode(NOT_FOUND.getStatusCode()); + var productDTOList = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + var product = productDTOList.get(0); + request.setBaseUrl("/mho-test"); + request.setMicrofrontends(new ArrayList<>()); + for (var mf : product.getMicrofrontends()) { + var updateDto = new UpdateMicrofrontendDTO(); + updateDto.setBasePath(mf.getBasePath()); + updateDto.setMfeId(mf.getMfeId()); + request.getMicrofrontends().add(updateDto); + } + request.getMicrofrontends().get(0).setBasePath("/mfe1-test"); + var dto = given() .when() .body(request) @@ -176,9 +206,25 @@ void updateProductByIdTest() { .extract().as(ProductDTO.class); assertThat(dto).isNotNull(); - assertThat(dto.getMicrofrontends()).isNull(); + assertThat(dto.getMicrofrontends()).isNotEmpty(); assertThat(dto.getBaseUrl()).isEqualTo(request.getBaseUrl()); + var response = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .get() + .then() + .statusCode(OK.getStatusCode()) + .extract().as(new TypeRef>() { + }); + + assertThat(response).isNotNull().isNotEmpty().hasSize(2); + var filteredProduct = response.stream().filter(x -> x.getProductName().equals(product.getProductName())).findFirst(); + assertThat(filteredProduct.get().getMicrofrontends()).isNotEmpty(); + assertThat(filteredProduct.get().getMicrofrontends().get(0).getBasePath()) + .isEqualTo(request.getMicrofrontends().get(0).getBasePath()); + request.setMicrofrontends(new ArrayList<>()); dto = given() .when() diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantIT.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantIT.java new file mode 100644 index 0000000..fb77f9a --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.workspace.rs.internal.controllers; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class WorkspaceInternalRestControllerTenantIT extends WorkspaceInternalRestControllerTenantTest { + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantTest.java new file mode 100644 index 0000000..8778234 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTenantTest.java @@ -0,0 +1,322 @@ +package io.github.onecx.workspace.rs.internal.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.internal.model.*; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@TestHTTPEndpoint(WorkspaceInternalRestController.class) +@WithDBData(value = "data/testdata-internal.xml", deleteBeforeInsert = true, deleteAfterTest = true, rinseAndRepeat = true) +class WorkspaceInternalRestControllerTenantTest extends AbstractTest { + + @Test + void createWorkspaceTest() { + + // create workspace + var createWorkspaceDTO = new CreateWorkspaceRequestDTO(); + createWorkspaceDTO + .workspaceName("Workspace1") + .companyName("Company1") + .baseUrl("/work1"); + + var dto = given() + .when() + .contentType(APPLICATION_JSON) + .body(createWorkspaceDTO) + .header(APM_HEADER_PARAM, createToken("org2")) + .post() + .then() + .statusCode(CREATED.getStatusCode()) + .extract().as(WorkspaceDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getWorkspaceName()).isNotNull().isEqualTo(createWorkspaceDTO.getWorkspaceName()); + assertThat(dto.getCompanyName()).isNotNull().isEqualTo(createWorkspaceDTO.getCompanyName()); + assertThat(dto.getBaseUrl()).isNotNull().isEqualTo(createWorkspaceDTO.getBaseUrl()); + + given() + .contentType(APPLICATION_JSON) + .pathParam("id", dto.getId()) + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{id}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + var workspaceDTO = given() + .contentType(APPLICATION_JSON) + .pathParam("id", dto.getId()) + .header(APM_HEADER_PARAM, createToken("org2")) + .get("{id}") + .then() + .statusCode(OK.getStatusCode()) + .extract().as(WorkspaceDTO.class); + assertThat(workspaceDTO).isNotNull(); + assertThat(workspaceDTO.getWorkspaceName()).isNotNull().isEqualTo(createWorkspaceDTO.getWorkspaceName()); + assertThat(workspaceDTO.getCompanyName()).isNotNull().isEqualTo(createWorkspaceDTO.getCompanyName()); + assertThat(workspaceDTO.getBaseUrl()).isNotNull().isEqualTo(createWorkspaceDTO.getBaseUrl()); + + // create without body + var exception = given() + .when() + .contentType(APPLICATION_JSON) + .header(APM_HEADER_PARAM, createToken("org1")) + .post() + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(exception.getErrorCode()).isEqualTo("CONSTRAINT_VIOLATIONS"); + assertThat(exception.getDetail()).isEqualTo("createWorkspace.createWorkspaceRequestDTO: must not be null"); + + exception = given() + .when() + .contentType(APPLICATION_JSON) + .body(createWorkspaceDTO) + .header(APM_HEADER_PARAM, createToken("org1")) + .post() + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(exception.getErrorCode()).isEqualTo("PERSIST_ENTITY_FAILED"); + assertThat(exception.getDetail()).isEqualTo( + "could not execute statement [ERROR: duplicate key value violates unique constraint 'workspace_base_url_key' Detail: Key (base_url)=(/work1) already exists.]"); + } + + @Test + void deleteWorkspace() { + given() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org2")) + .delete("{id}") + .then().statusCode(NO_CONTENT.getStatusCode()); + + given().contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{id}") + .then().statusCode(OK.getStatusCode()); + + given() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .delete("{id}") + .then().statusCode(NO_CONTENT.getStatusCode()); + + given().contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{id}") + .then().statusCode(NOT_FOUND.getStatusCode()); + + given() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .delete("{id}") + .then().statusCode(NO_CONTENT.getStatusCode()); + } + + @Test + void getWorkspace() { + given() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org2")) + .get("{id}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + given() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org3")) + .get("{id}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + var dto = given() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-111") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{id}") + .then() + .statusCode(OK.getStatusCode()) + .extract().as(WorkspaceDTO.class); + + assertThat(dto).isNotNull(); + assertThat(dto.getWorkspaceName()).isNotNull().isEqualTo("test01"); + assertThat(dto.getCompanyName()).isNotNull().isEqualTo("Company1"); + assertThat(dto.getBaseUrl()).isNotNull().isEqualTo("/company1"); + assertThat(dto.getAddress()).isNotNull(); + assertThat(dto.getAddress().getStreetNo()).isEqualTo("6"); + assertThat(dto.getImageUrls()).isNotEmpty(); + assertThat(dto.getSubjectLinks()).isNotEmpty(); + } + + @ParameterizedTest + @MethodSource("orgAndResults") + void searchByTenant(String organization, int results, int criteriaReults) { + var criteria = new WorkspaceSearchCriteriaDTO(); + + // empty criteria + var data = given() + .contentType(APPLICATION_JSON) + .body(criteria) + .header(APM_HEADER_PARAM, createToken(organization)) + .post("/search") + .then() + .statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .as(WorkspacePageResultDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getTotalElements()).isEqualTo(results); + assertThat(data.getStream()).isNotNull().hasSize(results); + + criteria.setWorkspaceName("test01"); + criteria.setThemeName("11-111"); + + data = given() + .contentType(APPLICATION_JSON) + .body(criteria) + .header(APM_HEADER_PARAM, createToken(organization)) + .post("/search") + .then() + .statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .as(WorkspacePageResultDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getTotalElements()).isEqualTo(criteriaReults); + assertThat(data.getStream()).isNotNull().hasSize(criteriaReults); + + criteria.setWorkspaceName(""); + criteria.setThemeName(""); + + data = given() + .contentType(APPLICATION_JSON) + .body(criteria) + .header(APM_HEADER_PARAM, createToken(organization)) + .post("/search") + .then() + .statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .as(WorkspacePageResultDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getTotalElements()).isEqualTo(results); + assertThat(data.getStream()).isNotNull().hasSize(results); + + criteria.setWorkspaceName(" "); + + data = given() + .contentType(APPLICATION_JSON) + .body(criteria) + .header(APM_HEADER_PARAM, createToken(organization)) + .post("/search") + .then() + .statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .as(WorkspacePageResultDTO.class); + + assertThat(data).isNotNull(); + assertThat(data.getTotalElements()).isZero(); + assertThat(data.getStream()).isNotNull().isEmpty(); + } + + private static Stream orgAndResults() { + return Stream.of( + arguments("org1", 2, 1), + arguments("org2", 1, 0), + arguments("org3", 0, 0)); + } + + @Test + void updateWorkspaceTest() { + var response = given().when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{id}") + .then().statusCode(OK.getStatusCode()) + .extract().as(WorkspaceDTO.class); + // update workspace with different tenant + given().when() + .contentType(APPLICATION_JSON) + .body(response) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org2")) + .put("{id}") + .then() + .statusCode(NOT_FOUND.getStatusCode()); + + // update workspace with already existing baseUrl for other workspace + response.setBaseUrl("/company3"); + var error = given().when() + .contentType(APPLICATION_JSON) + .body(response) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .put("{id}") + .then() + .statusCode(BAD_REQUEST.getStatusCode()) + .extract().as(ProblemDetailResponseDTO.class); + + assertThat(error).isNotNull(); + assertThat(error.getErrorCode()).isEqualTo("MERGE_ENTITY_FAILED"); + assertThat(error.getDetail()).isEqualTo( + "could not execute statement [ERROR: duplicate key value violates unique constraint 'workspace_base_url_key' Detail: Key (base_url)=(/company3) already exists.]"); + + // normal update + response.setBaseUrl("/company2/updated"); + response.setCompanyName("Company 2 updated"); + response.setWorkspaceName("Workspace2Test"); + given().when() + .contentType(APPLICATION_JSON) + .body(response) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .put("{id}") + .then() + .statusCode(NO_CONTENT.getStatusCode()); + + var updatedResponse = given().when() + .contentType(APPLICATION_JSON) + .pathParam("id", "11-222") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{id}") + .then().statusCode(OK.getStatusCode()) + .extract().as(WorkspaceDTO.class); + + assertThat(updatedResponse).isNotNull(); + assertThat(updatedResponse.getAddress()).isNotNull(); + assertThat(updatedResponse.getAddress().getStreetNo()).isEqualTo(response.getAddress().getStreetNo()); + assertThat(updatedResponse.getAddress().getStreet()).isEqualTo(response.getAddress().getStreet()); + assertThat(updatedResponse.getWorkspaceRoles()).isEqualTo(response.getWorkspaceRoles()); + assertThat(updatedResponse.getCompanyName()).isEqualTo(response.getCompanyName()); + assertThat(updatedResponse.getBaseUrl()).isEqualTo(response.getBaseUrl()); + } + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTest.java b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTest.java index 2537585..6521988 100644 --- a/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTest.java +++ b/src/test/java/io/github/onecx/workspace/rs/internal/controllers/WorkspaceInternalRestControllerTest.java @@ -6,8 +6,6 @@ import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; -import java.util.List; - import org.junit.jupiter.api.Test; import org.tkit.quarkus.test.WithDBData; @@ -75,18 +73,18 @@ void createWorkspaceTest() { void deleteWorkspace() { given() .contentType(APPLICATION_JSON) - .pathParam("id", "22-111") + .pathParam("id", "11-111") .delete("{id}") .then().statusCode(NO_CONTENT.getStatusCode()); given().contentType(APPLICATION_JSON) - .pathParam("id", "22-111") + .pathParam("id", "11-111") .get("{id}") .then().statusCode(NOT_FOUND.getStatusCode()); given() .contentType(APPLICATION_JSON) - .pathParam("id", "22-111") + .pathParam("id", "11-111") .delete("{id}") .then().statusCode(NO_CONTENT.getStatusCode()); } @@ -127,8 +125,8 @@ void searchWorkspacesTest() { .as(WorkspacePageResultDTO.class); assertThat(data).isNotNull(); - assertThat(data.getTotalElements()).isEqualTo(3); - assertThat(data.getStream()).isNotNull().hasSize(3); + assertThat(data.getTotalElements()).isEqualTo(2); + assertThat(data.getStream()).isNotNull().hasSize(2); criteria.setWorkspaceName("test01"); criteria.setThemeName("11-111"); @@ -161,8 +159,8 @@ void searchWorkspacesTest() { .as(WorkspacePageResultDTO.class); assertThat(data).isNotNull(); - assertThat(data.getTotalElements()).isEqualTo(3); - assertThat(data.getStream()).isNotNull().hasSize(3); + assertThat(data.getTotalElements()).isEqualTo(2); + assertThat(data.getStream()).isNotNull().hasSize(2); criteria.setWorkspaceName(" "); @@ -242,13 +240,4 @@ void updateWorkspaceTest() { assertThat(updatedResponse.getBaseUrl()).isEqualTo(response.getBaseUrl()); } - private UpdateWorkspaceRequestDTO createUpdateWorkspaceDTO() { - UpdateWorkspaceRequestDTO dto = new UpdateWorkspaceRequestDTO(); - - dto.setWorkspaceName("test-workspace-1"); - dto.setDescription("update workspace description"); - dto.setWorkspaceRoles(List.of("TestRole1", "TestRole2").toString()); - - return dto; - } } diff --git a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantIT.java b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantIT.java new file mode 100644 index 0000000..712177e --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.workspace.rs.legacy.controllers; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class PortalLegacyRestControllerTenantIT extends PortalLegacyRestControllerTenantTest { + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantTest.java new file mode 100644 index 0000000..0f5de5b --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/PortalLegacyRestControllerTenantTest.java @@ -0,0 +1,95 @@ +package io.github.onecx.workspace.rs.legacy.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.OK; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.legacy.model.MenuItemStructureDTO; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +@TestHTTPEndpoint(PortalLegacyRestController.class) +class PortalLegacyRestControllerTenantTest extends AbstractTest { + + @Test + void getMenuStructureForNoPortalNameTest() { + + var data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .header(APM_HEADER_PARAM, createToken("org3")) + .get("{portalName}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isEmpty(); + } + + @Test + @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) + void getMenuStructureForPortalNameTest() { + + var data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{portalName}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isNotEmpty().hasSize(5); + } + + @Test + @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) + void getMenuStructureForPortalNameAndAppTest() { + + var data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .pathParam("applicationId", "test01") + .header(APM_HEADER_PARAM, createToken("org1")) + .get("{portalName}/{applicationId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isNotEmpty().hasSize(5); + } + + @Test + @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) + void getMenuStructureForPortalNameAndAppDifferentTenantTest() { + + var data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .pathParam("applicationId", "test01") + .header(APM_HEADER_PARAM, createToken("org2")) + .get("{portalName}/{applicationId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isEmpty(); + } +} diff --git a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantIT.java b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantIT.java new file mode 100644 index 0000000..b2cbc41 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.workspace.rs.legacy.controllers; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class TkitPortalRestControllerTenantIT extends TkitPortalRestControllerTenantTest { + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantTest.java new file mode 100644 index 0000000..7bd3b77 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalRestControllerTenantTest.java @@ -0,0 +1,128 @@ +package io.github.onecx.workspace.rs.legacy.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.OK; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.legacy.model.MenuRegistrationRequestDTO; +import gen.io.github.onecx.workspace.rs.legacy.model.MenuRegistrationResponseDTO; +import gen.io.github.onecx.workspace.rs.legacy.model.ScopeDTO; +import gen.io.github.onecx.workspace.rs.legacy.model.TkitMenuItemStructureDTO; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +@TestHTTPEndpoint(TkitPortalRestController.class) +class TkitPortalRestControllerTenantTest extends AbstractTest { + + @Test + void getMenuStructureForPortalNameOrg3Test() { + + var data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .header(APM_HEADER_PARAM, createToken("org3")) + .get() + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isEmpty(); + } + + @Test + @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) + void getMenuStructureForPortalNameTest() { + + var data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .header(APM_HEADER_PARAM, createToken("org1")) + .get() + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isNotEmpty().hasSize(5); + + data = given() + .contentType(APPLICATION_JSON) + .pathParam("portalName", "test01") + .header(APM_HEADER_PARAM, createToken("org1")) + .queryParam("interpolate", Boolean.FALSE) + .get() + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(new TypeRef>() { + }); + + assertThat(data).isNotNull().isNotEmpty().hasSize(5); + } + + @Test + @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) + void submitMenuRegistrationRequestTest() { + var request = new MenuRegistrationRequestDTO(); + request.setRequestVersion(0); + var menuItems = new ArrayList(); + request.setMenuItems(menuItems); + var menuItem = new TkitMenuItemStructureDTO(); + menuItems.add(menuItem); + + menuItem.setKey("PARAMETERS_MANAGEMENT_UI_ROOT"); + menuItem.setName("Parameters management root menu item"); + menuItem.setUrl("/1000kit-parameters-ui"); + menuItem.setDisabled(false); + menuItem.setPosition(0); + menuItem.setScope(ScopeDTO.APP); + menuItem.setParentKey("PORTAL_MAIN_MENU"); + menuItem.setPermissionObject("PARAMETERS_MANAGEMENT_UI_ROOT"); + menuItem.setI18n(Map.of("de", "Parameters management", "en", "Parameters management")); + menuItem.setChildren(new ArrayList<>()); + + var subMenuItem = new TkitMenuItemStructureDTO(); + menuItem.getChildren().add(subMenuItem); + subMenuItem.setKey("PARAMETERS_MANAGEMENT_SEARCH"); + subMenuItem.setName("Parameters management search page menu item"); + subMenuItem.setUrl("/1000kit-parameters-ui/parameters/search"); + subMenuItem.setDisabled(false); + subMenuItem.setPosition(0); + subMenuItem.setScope(ScopeDTO.PAGE); + subMenuItem.setPortalExit(false); + subMenuItem.setParentKey("PARAMETERS_MANAGEMENT_UI_ROOT"); + subMenuItem.setPermissionObject("PARAMETER_SEARCH"); + subMenuItem.setI18n(Map.of("de", "Parameters Management Search", "en", "Parameters Management Search")); + + var response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test03") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org2")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isFalse(); + assertThat(response.getNotice()).isEqualTo("TKITPORTAL10003 Menu registration request has been ignored"); + } + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTenantTest.java b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTenantTest.java new file mode 100644 index 0000000..5fccf03 --- /dev/null +++ b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTenantTest.java @@ -0,0 +1,173 @@ +package io.github.onecx.workspace.rs.legacy.controllers; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.Status.OK; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.tkit.quarkus.test.WithDBData; + +import gen.io.github.onecx.workspace.rs.legacy.model.MenuRegistrationRequestDTO; +import gen.io.github.onecx.workspace.rs.legacy.model.MenuRegistrationResponseDTO; +import gen.io.github.onecx.workspace.rs.legacy.model.ScopeDTO; +import gen.io.github.onecx.workspace.rs.legacy.model.TkitMenuItemStructureDTO; +import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@TestHTTPEndpoint(TkitPortalRestController.class) +class TkitPortalSubmitMenuRequestTenantTest extends AbstractTest { + + @InjectMock + TkitLegacyAppConfig appConfig; + + @Test + @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) + void submitMenuRegistrationRequestTest() { + Mockito.when(appConfig.enableMenuAutoRegistration()).thenReturn(true); + var request = new MenuRegistrationRequestDTO(); + var response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "does-not-exist") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org3")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isFalse(); + + response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test03") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org3")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isFalse(); + + request.setMenuItems(new ArrayList<>()); + response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test03") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org3")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isFalse(); + + request = new MenuRegistrationRequestDTO(); + request.setRequestVersion(0); + var menuItems = new ArrayList(); + request.setMenuItems(menuItems); + var menuItem = new TkitMenuItemStructureDTO(); + menuItems.add(menuItem); + + menuItem.setKey("PARAMETERS_MANAGEMENT_UI_ROOT"); + menuItem.setName("Parameters management root menu item"); + menuItem.setUrl("/1000kit-parameters-ui"); + menuItem.setDisabled(false); + menuItem.setPosition(0); + menuItem.setScope(ScopeDTO.APP); + menuItem.setParentKey("PORTAL_MAIN_MENU"); + menuItem.setPermissionObject("PARAMETERS_MANAGEMENT_UI_ROOT"); + menuItem.setI18n(Map.of("de", "Parameters management", "en", "Parameters management")); + menuItem.setChildren(new ArrayList<>()); + + var subMenuItem = new TkitMenuItemStructureDTO(); + menuItem.getChildren().add(subMenuItem); + subMenuItem.setKey("PARAMETERS_MANAGEMENT_SEARCH"); + subMenuItem.setName("Parameters management search page menu item"); + subMenuItem.setUrl("/1000kit-parameters-ui/parameters/search"); + subMenuItem.setDisabled(false); + subMenuItem.setPosition(0); + subMenuItem.setScope(ScopeDTO.PAGE); + subMenuItem.setPortalExit(false); + subMenuItem.setParentKey("PARAMETERS_MANAGEMENT_UI_ROOT"); + subMenuItem.setPermissionObject("PARAMETER_SEARCH"); + subMenuItem.setI18n(Map.of("de", "Parameters Management Search", "en", "Parameters Management Search")); + + response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test01") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org3")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isFalse(); + + response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test01") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org1")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isTrue(); + + request.getMenuItems().get(0).setParentKey(null); + response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test01") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org1")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isTrue(); + + response = given() + .contentType(APPLICATION_JSON) + .body(request) + .pathParam("portalName", "test01") + .pathParam("appId", "parameters-management-ui") + .header(APM_HEADER_PARAM, createToken("org3")) + .post("{appId}") + .then().statusCode(OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(MenuRegistrationResponseDTO.class); + + assertThat(response).isNotNull(); + assertThat(response.getApplied()).isFalse(); + } + +} diff --git a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTest.java b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTest.java index 696eff5..521ecbc 100644 --- a/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTest.java +++ b/src/test/java/io/github/onecx/workspace/rs/legacy/controllers/TkitPortalSubmitMenuRequestTest.java @@ -8,7 +8,10 @@ import java.util.ArrayList; import java.util.Map; +import jakarta.enterprise.context.ApplicationScoped; + import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.tkit.quarkus.test.WithDBData; import gen.io.github.onecx.workspace.rs.legacy.model.MenuRegistrationRequestDTO; @@ -16,19 +19,22 @@ import gen.io.github.onecx.workspace.rs.legacy.model.ScopeDTO; import gen.io.github.onecx.workspace.rs.legacy.model.TkitMenuItemStructureDTO; import io.github.onecx.workspace.test.AbstractTest; +import io.quarkus.test.InjectMock; +import io.quarkus.test.Mock; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.QuarkusTestProfile; -import io.quarkus.test.junit.TestProfile; @QuarkusTest -@TestProfile(TkitPortalSubmitMenuRequestTest.CustomProfile.class) @TestHTTPEndpoint(TkitPortalRestController.class) class TkitPortalSubmitMenuRequestTest extends AbstractTest { + @InjectMock + TkitLegacyAppConfig appConfig; + @Test @WithDBData(value = "data/testdata-legacy.xml", deleteAfterTest = true, deleteBeforeInsert = true) void submitMenuRegistrationRequestTest() { + Mockito.when(appConfig.enableMenuAutoRegistration()).thenReturn(true); var request = new MenuRegistrationRequestDTO(); var response = given() .contentType(APPLICATION_JSON) @@ -105,7 +111,7 @@ void submitMenuRegistrationRequestTest() { response = given() .contentType(APPLICATION_JSON) .body(request) - .pathParam("portalName", "test03") + .pathParam("portalName", "test01") .pathParam("appId", "parameters-management-ui") .post("{appId}") .then().statusCode(OK.getStatusCode()) @@ -120,7 +126,7 @@ void submitMenuRegistrationRequestTest() { response = given() .contentType(APPLICATION_JSON) .body(request) - .pathParam("portalName", "test03") + .pathParam("portalName", "test01") .pathParam("appId", "parameters-management-ui") .post("{appId}") .then().statusCode(OK.getStatusCode()) @@ -132,17 +138,13 @@ void submitMenuRegistrationRequestTest() { assertThat(response.getApplied()).isTrue(); } - public static class CustomProfile implements QuarkusTestProfile { - - @Override - public String getConfigProfile() { - return "test"; - } + @ApplicationScoped + @Mock + public static class MockedAppConfig implements TkitLegacyAppConfig { @Override - public Map getConfigOverrides() { - return Map.of( - "tkit.legacy.enable.menu.auto.registration", "true"); + public boolean enableMenuAutoRegistration() { + return false; } } diff --git a/src/test/java/io/github/onecx/workspace/test/AbstractTest.java b/src/test/java/io/github/onecx/workspace/test/AbstractTest.java index 073e54f..8adbcaf 100644 --- a/src/test/java/io/github/onecx/workspace/test/AbstractTest.java +++ b/src/test/java/io/github/onecx/workspace/test/AbstractTest.java @@ -4,14 +4,29 @@ import static io.restassured.RestAssured.config; import static io.restassured.config.ObjectMapperConfig.objectMapperConfig; +import java.security.PrivateKey; + +import jakarta.json.Json; +import jakarta.json.JsonObjectBuilder; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.jwt.Claims; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.restassured.config.RestAssuredConfig; +import io.smallrye.jwt.build.Jwt; +import io.smallrye.jwt.util.KeyUtils; @SuppressWarnings("java:S2187") public class AbstractTest { + protected static final String APM_HEADER_PARAM = ConfigProvider.getConfig() + .getValue("%test.tkit.rs.context.tenant-id.mock.token-header-param", String.class); + protected static final String CLAIMS_ORG_ID = ConfigProvider.getConfig() + .getValue("%test.tkit.rs.context.tenant-id.mock.claim-org-id", String.class);; + static { config = RestAssuredConfig.config().objectMapperConfig( objectMapperConfig().jackson2ObjectMapperFactory( @@ -22,4 +37,18 @@ public class AbstractTest { return objectMapper; })); } + + protected static String createToken(String organizationId) { + try { + String userName = "test-user"; + JsonObjectBuilder claims = Json.createObjectBuilder(); + claims.add(Claims.preferred_username.name(), userName); + claims.add(Claims.sub.name(), userName); + claims.add(CLAIMS_ORG_ID, organizationId); + PrivateKey privateKey = KeyUtils.generateKeyPair(2048).getPrivate(); + return Jwt.claims(claims.build()).sign(privateKey); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } } diff --git a/src/test/resources/data/testdata-external.xml b/src/test/resources/data/testdata-external.xml index 5ed353c..12f7bdd 100644 --- a/src/test/resources/data/testdata-external.xml +++ b/src/test/resources/data/testdata-external.xml @@ -3,9 +3,14 @@ - - - - - + + + + + + + + + + diff --git a/src/test/resources/data/testdata-internal.xml b/src/test/resources/data/testdata-internal.xml index 86b2f2d..59df3c8 100644 --- a/src/test/resources/data/testdata-internal.xml +++ b/src/test/resources/data/testdata-internal.xml @@ -3,40 +3,40 @@ - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -47,8 +47,8 @@ - - + + diff --git a/src/test/resources/data/testdata-legacy.xml b/src/test/resources/data/testdata-legacy.xml index e9058a5..60f829d 100644 --- a/src/test/resources/data/testdata-legacy.xml +++ b/src/test/resources/data/testdata-legacy.xml @@ -3,34 +3,34 @@ - - - + + + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/import/workspace-import.json b/src/test/resources/import/workspace-import.json index f37853f..e43c086 100644 --- a/src/test/resources/import/workspace-import.json +++ b/src/test/resources/import/workspace-import.json @@ -19,8 +19,7 @@ "name": "Welcome Page", "parentItemId": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", "parentKey": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 0, "scope": "WORKSPACE", "url": "/admin/" @@ -44,8 +43,7 @@ "name": "Announcements", "parentItemId": "156a8cff-f341-47bf-a1fc-dede091ee7c0", "parentKey": "156a8cff-f341-47bf-a1fc-dede091ee7c0", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 0, "scope": "WORKSPACE", "url": "/admin/ah-mgmt/announcements" @@ -64,8 +62,7 @@ "name": "Help Items", "parentItemId": "156a8cff-f341-47bf-a1fc-dede091ee7c0", "parentKey": "156a8cff-f341-47bf-a1fc-dede091ee7c0", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 0, "scope": "WORKSPACE", "url": "/admin/ah-mgmt/helpitems/" @@ -81,8 +78,7 @@ "name": "Announcement & Help Management", "parentItemId": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", "parentKey": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 6, "scope": "WORKSPACE", "url": "/admin/ah-mgmt/" @@ -106,8 +102,7 @@ "name": "Menu Management", "parentItemId": "3c94b43b-3fdf-4ed6-89c5-6e6e215a022a", "parentKey": "3c94b43b-3fdf-4ed6-89c5-6e6e215a022a", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 3, "scope": "WORKSPACE", "url": "/admin/portal-mgmt/menu/" @@ -126,8 +121,7 @@ "name": "Theme Management", "parentItemId": "3c94b43b-3fdf-4ed6-89c5-6e6e215a022a", "parentKey": "3c94b43b-3fdf-4ed6-89c5-6e6e215a022a", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 0, "scope": "WORKSPACE", "url": "/admin/portal-mgmt/theme/" @@ -146,8 +140,7 @@ "name": "Portal Management", "parentItemId": "3c94b43b-3fdf-4ed6-89c5-6e6e215a022a", "parentKey": "3c94b43b-3fdf-4ed6-89c5-6e6e215a022a", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 2, "scope": "WORKSPACE", "url": "/admin/portal-mgmt/portal/" @@ -162,8 +155,7 @@ "name": "Portal", "parentItemId": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", "parentKey": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 2, "scope": "WORKSPACE", "url": "" @@ -182,8 +174,7 @@ "name": "MFE Config", "parentItemId": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", "parentKey": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 98, "scope": "WORKSPACE", "url": "/admin/config/" @@ -193,8 +184,7 @@ "i18n": {}, "key": "PORTAL_MAIN_MENU", "name": "Main Menu", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 0, "scope": "WORKSPACE" }, @@ -215,8 +205,7 @@ "name": "Personal Info", "parentItemId": "92037049-3b2a-4539-bb06-61b5e441474c", "parentKey": "92037049-3b2a-4539-bb06-61b5e441474c", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 1, "scope": "WORKSPACE", "url": "/admin/account/profile" @@ -235,8 +224,7 @@ "name": "Account Settings", "parentItemId": "92037049-3b2a-4539-bb06-61b5e441474c", "parentKey": "92037049-3b2a-4539-bb06-61b5e441474c", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 2, "scope": "WORKSPACE", "url": "/admin/account/settings" @@ -255,8 +243,7 @@ "name": "Change Password", "parentItemId": "92037049-3b2a-4539-bb06-61b5e441474c", "parentKey": "92037049-3b2a-4539-bb06-61b5e441474c", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 3, "scope": "WORKSPACE", "url": "/admin/account/change-password" @@ -275,8 +262,7 @@ "name": "Roles & Permissions", "parentItemId": "92037049-3b2a-4539-bb06-61b5e441474c", "parentKey": "92037049-3b2a-4539-bb06-61b5e441474c", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 4, "scope": "WORKSPACE", "url": "/admin/account/roles-and-perms" @@ -286,8 +272,7 @@ "i18n": {}, "key": "USER_PROFILE_MENU", "name": "User Profile Menu", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 5, "scope": "WORKSPACE" }, @@ -298,8 +283,7 @@ "i18n": {}, "key": "PORTAL_ADMIN_MENU", "name": "Admin Menu", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 2, "scope": "WORKSPACE" }, @@ -309,8 +293,7 @@ "i18n": {}, "key": "PORTAL_FOOTER_MENU", "name": "Footer Menu", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 2, "scope": "WORKSPACE", "children": [ @@ -325,8 +308,7 @@ "name": "Contact", "parentItemId": "2bc91211-95df-4d2d-b56d-b9ece916d956", "parentKey": "PORTAL_FOOTER_MENU", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 1, "scope": "WORKSPACE", "url": "/contact" @@ -342,8 +324,7 @@ "name": "Impressum", "parentItemId": "2bc91211-95df-4d2d-b56d-b9ece916d956", "parentKey": "PORTAL_FOOTER_MENU", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 2, "scope": "WORKSPACE", "url": "/Impressum" @@ -357,8 +338,7 @@ "i18n": {}, "key": "PORTAL_EXTERNAL_NAV", "name": "External Navigation Menu", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 4, "scope": "WORKSPACE" }, @@ -369,8 +349,7 @@ "i18n": {}, "key": "PORTAL_MENU_HOME", "name": "Home Menu", - "portalExit": false, - "portalId": "ADMIN", + "workspaceExit": false, "position": 0, "scope": "WORKSPACE", "url": "/admin/" @@ -379,6 +358,7 @@ "workspace": { "baseUrl": "/admin", "companyName": "Company", + "tenantId": "tenant-100", "description": "Portal zur Administration von Portalen", "footerLabel": "Admin Portal", "homePage": "/admin", @@ -406,6 +386,63 @@ ], "themeName": "Theme1" } - } + }, + { + "menuItems": [ + { + "children": [ + { + "applicationId": "portal-welcome-ui", + "badge": "home", + "children": [], + "disabled": false, + "i18n": { + "de": "Portal Startseite", + "en": "Portal Home" + }, + "key": "CORE_WELCOME", + "name": "Welcome Page", + "parentItemId": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", + "parentKey": "b6da299d-f7da-4ba9-89c6-cdb976a998ab", + "workspaceExit": false, + "position": 0, + "scope": "WORKSPACE", + "url": "/tkom/" + } + ] + } + ], + "workspace": { + "baseUrl": "/tkom", + "companyName": "Company2", + "tenantId": "default", + "description": "Portal zur Core von Portalen", + "footerLabel": "Core Portal", + "homePage": "/tkom", + "workspaceName": "TKOM", + "workspaceRoles": [ + "onecx-portal-user", + "onecx-portal-admin", + "onecx-portal-super-admin" + ], + "products": [ + { + "baseUrl" : "/core", + "productName": "onecx-core", + "microfrontends": [ + { + "mfeId": "menu", + "basePath": "/menu" + }, + { + "mfeId": "theme", + "basePath": "/theme" + } + ] + } + ], + "themeName": "Theme1" + } + } ] }