diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java index 3527d9d1..5b979216 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java @@ -26,7 +26,7 @@ import java.util.UUID; import java.util.function.BinaryOperator; import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.joining; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange; @@ -51,9 +51,18 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject { .withProp(e -> e.getDebitor().toShortString()) .withProp(e -> e.getValidity().asString()) .withProp(HsBookingItemEntity::getCaption) - .withProp(HsBookingItemEntity::getResources) + .withProp(HsBookingItemEntity::getResourcesAsString) .quotedValues(false); + private String getResourcesAsString() { + return "{ " + + ( + resources.keySet().stream().sorted() + .map(k -> k + ": " + resources.get(k))) + .collect(joining(", ") + ) + " }"; + } + @Id @GeneratedValue private UUID uuid; @@ -78,11 +87,6 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject { @Column(columnDefinition = "resources") private Map resources = new TreeMap<>(); - public Map getResources() { - return resources.entrySet().stream() - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, HsBookingItemEntity::thereIsOnlyOneValuePerKey, TreeMap::new)); - } - public void setValidFrom(final LocalDate validFrom) { setValidity(toPostgresDateRange(validFrom, getValidTo())); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcher.java index 865e16c8..491fea11 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcher.java @@ -4,10 +4,11 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.ArbitraryBook import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource; import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.OptionalFromJson; +import org.apache.commons.lang3.tuple.ImmutablePair; +import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import static java.util.Arrays.stream; @@ -38,16 +39,24 @@ public class HsBookingItemEntityPatcher implements EntityPatcher objectToMap(final Object obj) { - final var map = stream(obj.getClass().getDeclaredFields()) + return stream(obj.getClass().getDeclaredFields()) .map(field -> { try { field.setAccessible(true); - return Map.entry(field.getName(), field.get(obj)); + return new ImmutablePair<>(field.getName(), field.get(obj)); } catch (final IllegalAccessException exc) { throw new RuntimeException(exc); } }) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - return map; + .reduce( + new HashMap<>(), + (map, pair) -> { + map.put(pair.getKey(), pair.getValue()); + return map; + }, + (map1, map2) -> { + map1.putAll(map2); + return map1; + }); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index c22455a4..4ec6685d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -18,7 +18,6 @@ import jakarta.persistence.*; import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.Optional; import java.util.UUID; import static java.util.Optional.ofNullable; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java index e053843f..9a3295a2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java @@ -2,9 +2,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import jakarta.persistence.EntityNotFoundException; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi; -import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource; import net.hostsharing.hsadminng.mapper.Mapper; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index c9f334e6..8604ec16 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -6,7 +6,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.errors.DisplayName; -import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java index 0ce06015..da88a87e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java @@ -148,6 +148,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup context.define("superuser-alex@hostsharing.net"); final var givenBookingItemUuid = bookingItemRepo.findAll().stream() .filter(bi -> bi.getDebitor().getDebitorNumber() == 1000111) + .filter(item -> item.getCaption().equals("some CloudServer")) .findAny().orElseThrow().getUuid(); RestAssured // @formatter:off @@ -192,8 +193,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup context.define("superuser-alex@hostsharing.net"); final var givenBookingItemUuid = bookingItemRepo.findAll().stream() .filter(bi -> bi.getDebitor().getDebitorNumber() == 1000313) + .filter(item -> item.getCaption().equals("some CloudServer")) .findAny().orElseThrow().getUuid(); - generateRbacDiagramForObjectPermission(givenBookingItemUuid, "SELECT", "booking-item-of-debitor-1000313"); RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcherUnitTest.java index 8bebf69f..c29edc4c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityPatcherUnitTest.java @@ -35,12 +35,17 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase< private static final LocalDate PATCHED_VALID_FROM = LocalDate.parse("2022-10-30"); private static final LocalDate PATCHED_VALID_TO = LocalDate.parse("2022-12-31"); - private static final ArbitraryBookingResourcesJsonResource PATCHED_RESOURCES = new ArbitraryBookingResourcesJsonResource() { - int cpus = 2; - int sddStorage = 256; - int hddStorage = 2048; + private static final ArbitraryBookingResourcesJsonResource INITIAL_RESOURCES = new ArbitraryBookingResourcesJsonResource() { + Integer cpus = 1; + Integer hddStorage = 1024; }; - private static final String PATCHED_CAPTION = "caption-patched"; + private static final ArbitraryBookingResourcesJsonResource PATCHED_RESOURCES = new ArbitraryBookingResourcesJsonResource() { + Integer cpus = 2; + Integer sddStorage = 256; + Integer hddStorage = null; + }; + private static final String INITIAL_CAPTION = "initial caption"; + private static final String PATCHED_CAPTION = "patched caption"; @Mock private EntityManager em; @@ -58,8 +63,8 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase< final var entity = new HsBookingItemEntity(); entity.setUuid(INITIAL_BOOKING_ITEM_UUID); entity.setDebitor(TEST_DEBITOR); - entity.setResources(objectToMap(PATCHED_RESOURCES)); - entity.setCaption(PATCHED_CAPTION); + entity.setResources(objectToMap(INITIAL_RESOURCES)); + entity.setCaption(INITIAL_CAPTION); entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM)); return entity; } @@ -82,13 +87,13 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase< HsBookingItemPatchResource::setCaption, PATCHED_CAPTION, HsBookingItemEntity::setCaption), -// FIXME -// new JsonNullableProperty<>( -// "resources", -// HsBookingItemPatchResource::setResources, -// PATCHED_RESOURCES, -// HsBookingItemEntity::setResources, -// objectToMap(PATCHED_RESOURCES)), + new SimpleProperty<>( + "resources", + HsBookingItemPatchResource::setResources, + PATCHED_RESOURCES, + HsBookingItemEntity::setResources, + objectToMap(PATCHED_RESOURCES)) + .notNullable(), new JsonNullableProperty<>( "validfrom", HsBookingItemPatchResource::setValidFrom, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityUnitTest.java index ce85e7fc..20bad4eb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityUnitTest.java @@ -28,7 +28,7 @@ class HsBookingItemEntityUnitTest { void toStringContainsAllPropertiesAndResourcesSortedByKey() { final var result = givenBookingItem.toString(); - assertThat(result).isEqualTo("HsBookingItemEntity(D-1000100, [2020-01-01,2031-01-01), some caption, {CPUs=2, HDD-storage=2048, SSD-storage=512})"); + assertThat(result).isEqualTo("HsBookingItemEntity(D-1000100, [2020-01-01,2031-01-01), some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java index 518a0f6b..ddf62936 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java @@ -109,16 +109,16 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup final var all = rawRoleRepo.findAll(); assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_booking_item#somenewbookingitem:ADMIN", - "hs_booking_item#somenewbookingitem:OWNER", - "hs_booking_item#somenewbookingitem:TENANT")); + "hs_booking_item#D-1000111:some new booking item:ADMIN", + "hs_booking_item#D-1000111:some new booking item:OWNER", + "hs_booking_item#D-1000111:some new booking item:TENANT")); assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(fromFormatted( initialGrantNames, // insert+delete - "{ grant perm:hs_booking_item#somenewbookingitem:DELETE to role:global#global:ADMIN by system and assume }", + "{ grant perm:hs_booking_item#D-1000111:some new booking item:DELETE to role:global#global:ADMIN by system and assume }", // owner @@ -126,12 +126,12 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup // tenant - "{ grant perm:hs_booking_item#somenewbookingitem:SELECT to role:hs_booking_item#somenewbookingitem:TENANT by system and assume }", - "{ grant perm:hs_booking_item#somenewbookingitem:UPDATE to role:hs_booking_item#somenewbookingitem:OWNER by system and assume }", - "{ grant role:hs_booking_item#somenewbookingitem:ADMIN to role:hs_booking_item#somenewbookingitem:OWNER by system and assume }", - "{ grant role:hs_booking_item#somenewbookingitem:OWNER to role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:AGENT by system and assume }", - "{ grant role:hs_booking_item#somenewbookingitem:TENANT to role:hs_booking_item#somenewbookingitem:ADMIN by system and assume }", - "{ grant role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:TENANT to role:hs_booking_item#somenewbookingitem:TENANT by system and assume }", + "{ grant perm:hs_booking_item#D-1000111:some new booking item:SELECT to role:hs_booking_item#D-1000111:some new booking item:TENANT by system and assume }", + "{ grant perm:hs_booking_item#D-1000111:some new booking item:UPDATE to role:hs_booking_item#D-1000111:some new booking item:OWNER by system and assume }", + "{ grant role:hs_booking_item#D-1000111:some new booking item:ADMIN to role:hs_booking_item#D-1000111:some new booking item:OWNER by system and assume }", + "{ grant role:hs_booking_item#D-1000111:some new booking item:OWNER to role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:AGENT by system and assume }", + "{ grant role:hs_booking_item#D-1000111:some new booking item:TENANT to role:hs_booking_item#D-1000111:some new booking item:ADMIN by system and assume }", + "{ grant role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:TENANT to role:hs_booking_item#D-1000111:some new booking item:TENANT by system and assume }", null)); } @@ -148,7 +148,8 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup public void globalAdmin_withoutAssumedRole_canViewAllBookingItemsOfArbitraryDebitor() { // given context("superuser-alex@hostsharing.net"); - final var debitorUuid = debitorRepo.findDebitorByDebitorNumber(1000212).stream().findAny().orElseThrow().getUuid(); + final var debitorUuid = debitorRepo.findDebitorByDebitorNumber(1000212).stream() + .findAny().orElseThrow().getUuid(); // when final var result = bookingItemRepo.findAllByDebitorUuid(debitorUuid); @@ -156,9 +157,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup // then allTheseBookingItemsAreReturned( result, - "HsBookingItemEntity(D-1000212, [2023-01-15,2024-04-15), some CloudServer, {CPUs=2, HDD-storage=1024})", - "HsBookingItemEntity(D-1000212, [2022-10-01,), some ManagedServer, {CPUs=2, SDD-storage=512})", - "HsBookingItemEntity(D-1000212, [2024-04-01,), some Whatever, {CPUs=1, HDD-storage=2048, SDD-storage=512})"); + "HsBookingItemEntity(D-1000212, [2022-10-01,), some ManagedServer, { CPUs: 2, SDD-storage: 512 })", + "HsBookingItemEntity(D-1000212, [2023-01-15,2024-04-15), some CloudServer, { CPUs: 2, HDD-storage: 1024 })", + "HsBookingItemEntity(D-1000212, [2024-04-01,), some Whatever, { CPUs: 1, HDD-storage: 2048, SDD-storage: 512 })"); } @Test @@ -173,9 +174,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup // then: exactlyTheseBookingItemsAreReturned( result, - "HsBookingItemEntity(D-1000111, [2023-01-15,2024-04-15), some CloudServer, {CPUs=2, HDD-storage=1024})", - "HsBookingItemEntity(D-1000111, [2022-10-01,), some ManagedServer, {CPUs=2, SDD-storage=512})", - "HsBookingItemEntity(D-1000111, [2024-04-01,), some Whatever, {CPUs=1, HDD-storage=2048, SDD-storage=512})"); + "HsBookingItemEntity(D-1000111, [2022-10-01,), some ManagedServer, { CPUs: 2, SDD-storage: 512 })", + "HsBookingItemEntity(D-1000111, [2023-01-15,2024-04-15), some CloudServer, { CPUs: 2, HDD-storage: 1024 })", + "HsBookingItemEntity(D-1000111, [2024-04-01,), some Whatever, { CPUs: 1, HDD-storage: 2048, SDD-storage: 512 })"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java index 44ade22c..08a2718d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java @@ -1,10 +1,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; -import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; -import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType; import org.junit.jupiter.api.Test; -import java.math.BigDecimal; import java.time.LocalDate; import static net.hostsharing.hsadminng.hs.office.membership.TestHsMembership.TEST_MEMBERSHIP;