introduce-booking-module #41
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.booking.item;
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType;
|
||||
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
@ -10,31 +11,44 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Version;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BinaryOperator;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static net.hostsharing.hsadminng.mapper.PatchableMap.mapToString;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -51,18 +65,9 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
||||
.withProp(e -> e.getDebitor().toShortString())
|
||||
.withProp(e -> e.getValidity().asString())
|
||||
.withProp(HsBookingItemEntity::getCaption)
|
||||
.withProp(HsBookingItemEntity::getResourcesAsString)
|
||||
.withProp(e -> mapToString(e.getResources()))
|
||||
.quotedValues(false);
|
||||
|
||||
private String getResourcesAsString() {
|
||||
return "{ " +
|
||||
(
|
||||
resources.keySet().stream().sorted()
|
||||
.map(k -> k + ": " + resources.get(k)))
|
||||
.collect(joining(", ")
|
||||
) + " }";
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID uuid;
|
||||
@ -83,6 +88,7 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
||||
private String caption;
|
||||
|
||||
@Builder.Default
|
||||
@Setter(AccessLevel.NONE)
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "resources")
|
||||
private Map<String, Object> resources = new TreeMap<>();
|
||||
@ -103,7 +109,6 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
||||
return upperInclusiveFromPostgresDateRange(getValidity());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringify.apply(this);
|
||||
@ -115,17 +120,13 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
||||
":" + caption;
|
||||
}
|
||||
|
||||
private static BinaryOperator<Object> thereIsOnlyOneValuePerKey(Object o, Object o1) {
|
||||
return (a, b) -> a;
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("bookingItem", HsBookingItemEntity.class)
|
||||
.withIdentityView(SQL.query("""
|
||||
SELECT i.uuid as uuid, d.idName || ':' || i.caption as idName
|
||||
FROM hs_booking_item i
|
||||
JOIN hs_office_debitor_iv d ON d.uuid = i.debitorUuid
|
||||
"""))
|
||||
SELECT i.uuid as uuid, d.idName || ':' || i.caption as idName
|
||||
FROM hs_booking_item i
|
||||
JOIN hs_office_debitor_iv d ON d.uuid = i.debitorUuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.expression("validity"))
|
||||
.withUpdatableColumns("version", "validity", "resources")
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
package net.hostsharing.hsadminng.mapper;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
public class PatchableMap {
|
||||
|
||||
@SafeVarargs
|
||||
public static Map<String, Object> patchableMap(final ImmutablePair<String, Object>... entries) {
|
||||
final var map = new HashMap<String, Object>();
|
||||
Arrays.stream(entries).forEach(r -> map.put(r.getKey(), r.getValue()));
|
||||
return map;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ImmutablePair<String, Object> entry(final String key, final Object value) {
|
||||
return new ImmutablePair<>(key, value);
|
||||
}
|
||||
|
||||
public static BiConsumer<? extends Object, ? super Map<String, Object>> assignMap = (HsBookingItemEntity i, Map<String, Object> r) -> {
|
||||
i.getResources().clear();
|
||||
i.getResources().putAll(r);
|
||||
};
|
||||
|
||||
public static String mapToString(final Map<String, Object> resources) {
|
||||
return "{ "
|
||||
+ (
|
||||
resources.keySet().stream().sorted()
|
||||
.map(k -> k + ": " + resources.get(k)))
|
||||
.collect(joining(", ")
|
||||
)
|
||||
+ " }";
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import io.hypersistence.utils.hibernate.type.range.Range;
|
||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.ArbitraryBookingResourcesJsonResource;
|
||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||
import net.hostsharing.hsadminng.mapper.PatchableMap;
|
||||
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
@ -63,7 +64,7 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
final var entity = new HsBookingItemEntity();
|
||||
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
||||
entity.setDebitor(TEST_DEBITOR);
|
||||
entity.setResources(objectToMap(INITIAL_RESOURCES));
|
||||
entity.getResources().putAll(objectToMap(INITIAL_RESOURCES));
|
||||
entity.setCaption(INITIAL_CAPTION);
|
||||
entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM));
|
||||
return entity;
|
||||
@ -91,7 +92,7 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
"resources",
|
||||
HsBookingItemPatchResource::setResources,
|
||||
PATCHED_RESOURCES,
|
||||
HsBookingItemEntity::setResources,
|
||||
PatchableMap.assignMap,
|
||||
objectToMap(PATCHED_RESOURCES))
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
|
@ -22,9 +22,9 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
import static net.hostsharing.hsadminng.mapper.PatchableMap.entry;
|
||||
import static net.hostsharing.hsadminng.mapper.PatchableMap.patchableMap;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
|
||||
import static net.hostsharing.hsadminng.rbac.test.Array.fromFormatted;
|
||||
@ -186,18 +186,18 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
@Test
|
||||
public void hostsharingAdmin_canUpdateArbitraryBookingItem() {
|
||||
// given
|
||||
final var givenBookingItem = givenSomeTemporaryBookingItem(1000111);
|
||||
final var givenBookingItemUuid = givenSomeTemporaryBookingItem(1000111).getUuid();
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
context("superuser-alex@hostsharing.net");
|
||||
givenBookingItem.setResources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("SSD-storage", 512),
|
||||
entry("HDD-storage", 2048)));
|
||||
givenBookingItem.setValidity(Range.closedOpen(
|
||||
final var foundBookingItem = em.find(HsBookingItemEntity.class, givenBookingItemUuid);
|
||||
foundBookingItem.getResources().put("CPUs", 2);
|
||||
foundBookingItem.getResources().remove("SSD-storage");
|
||||
foundBookingItem.getResources().put("HSD-storage", 2048);
|
||||
foundBookingItem.setValidity(Range.closedOpen(
|
||||
LocalDate.parse("2019-05-17"), LocalDate.parse("2023-01-01")));
|
||||
return toCleanup(bookingItemRepo.save(givenBookingItem));
|
||||
return toCleanup(bookingItemRepo.save(foundBookingItem));
|
||||
});
|
||||
|
||||
// then
|
||||
@ -312,7 +312,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
.caption("some temp booking item")
|
||||
.validity(Range.closedOpen(
|
||||
LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01")))
|
||||
.resources(Map.ofEntries(
|
||||
.resources(patchableMap(
|
||||
entry("CPUs", 1),
|
||||
entry("SSD-storage", 256)))
|
||||
.build();
|
||||
|
Loading…
Reference in New Issue
Block a user