introduce booking-item-type and check (#51)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #51
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig 2024-05-02 13:53:53 +02:00
parent e09a09cf92
commit c953b815d5
11 changed files with 125 additions and 30 deletions

View File

@ -21,6 +21,8 @@ import org.hibernate.annotations.Type;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
@ -66,7 +68,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsBookingItemEntity implements Stringifyable, RbacObject {
private static Stringify<HsBookingItemEntity> stringify = stringify(HsBookingItemEntity.class)
.withProp(e -> e.getDebitor().toShortString())
.withProp(HsBookingItemEntity::getDebitor)
.withProp(HsBookingItemEntity::getType)
.withProp(e -> e.getValidity().asString())
.withProp(HsBookingItemEntity::getCaption)
.withProp(HsBookingItemEntity::getResources)
@ -83,6 +86,10 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
@JoinColumn(name = "debitoruuid")
private HsOfficeDebitorEntity debitor;
@Column(name = "type")
@Enumerated(EnumType.STRING)
private HsBookingItemType type;
@Builder.Default
@Type(PostgreSQLRangeType.class)
@Column(name = "validity", columnDefinition = "daterange")

View File

@ -0,0 +1,8 @@
package net.hostsharing.hsadminng.hs.booking.item;
public enum HsBookingItemType {
PRIVATE_CLOUD,
CLOUD_SERVER,
MANAGED_SERVER,
MANAGED_WEBSPACE
}

View File

@ -3,12 +3,22 @@ components:
schemas:
HsBookingItemType:
type: string
enum:
- PRIVATE_CLOUD
- CLOUD_SERVER
- MANAGED_SERVER
- MANAGED_WEBSPACE
HsBookingItem:
type: object
properties:
uuid:
type: string
format: uuid
type:
$ref: '#/components/schemas/HsBookingItemType'
caption:
type: string
validFrom:
@ -45,6 +55,8 @@ components:
type: string
format: uuid
nullable: false
type:
$ref: '#/components/schemas/HsBookingItemType'
caption:
type: string
minLength: 3

View File

@ -4,11 +4,21 @@
--changeset booking-item-MAIN-TABLE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create type HsBookingItemType as enum (
'PRIVATE_CLOUD',
'CLOUD_SERVER',
'MANAGED_SERVER',
'MANAGED_WEBSPACE'
);
CREATE CAST (character varying as HsBookingItemType) WITH INOUT AS IMPLICIT;
create table if not exists hs_booking_item
(
uuid uuid unique references RbacObject (uuid),
version int not null default 0,
debitorUuid uuid not null references hs_office_debitor(uuid),
type HsBookingItemType not null,
validity daterange not null,
caption varchar(80) not null,
resources jsonb not null

View File

@ -31,10 +31,10 @@ begin
raise notice 'creating test booking-item: %', givenPartnerNumber::text || givenDebitorSuffix::text;
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
insert
into hs_booking_item (uuid, debitoruuid, caption, validity, resources)
values (uuid_generate_v4(), relatedDebitor.uuid, 'some ManagedServer', daterange('20221001', null, '[]'), '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedDebitor.uuid, 'some CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedDebitor.uuid, 'some PrivateCloud', daterange('20240401', null, '[]'), '{ "CPU": 10, "SDD": 10240, "HDD": 10240, "extra": 42 }'::jsonb);
into hs_booking_item (uuid, debitoruuid, type, caption, validity, resources)
values (uuid_generate_v4(), relatedDebitor.uuid, 'MANAGED_SERVER', 'some ManagedServer', daterange('20221001', null, '[]'), '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedDebitor.uuid, 'CLOUD_SERVER', 'some CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedDebitor.uuid, 'PRIVATE_CLOUD', 'some PrivateCloud', daterange('20240401', null, '[]'), '{ "CPU": 10, "SDD": 10240, "HDD": 10240, "extra": 42 }'::jsonb);
end; $$;
--//
@ -50,3 +50,4 @@ do language plpgsql $$
call createHsBookingItemTransactionTestData(10003, '13');
end;
$$;
--//

View File

@ -25,7 +25,7 @@ create table if not exists hs_hosting_asset
uuid uuid unique references RbacObject (uuid),
version int not null default 0,
bookingItemUuid uuid not null references hs_booking_item(uuid),
type HsHostingAssetType,
type HsHostingAssetType not null,
parentAssetUuid uuid null references hs_hosting_asset(uuid),
identifier varchar(80) not null,
caption varchar(80) not null,
@ -35,7 +35,7 @@ create table if not exists hs_hosting_asset
-- ============================================================================
--changeset hosting-asset-HIERARCHY-CHECK:1 endDelimiter:--//
--changeset hosting-asset-TYPE-HIERARCHY-CHECK:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create or replace function hs_hosting_asset_type_hierarchy_check_tf()
@ -83,6 +83,47 @@ create trigger hs_hosting_asset_type_hierarchy_check_tg
--//
-- ============================================================================
--changeset hosting-asset-BOOKING-ITEM-HIERARCHY-CHECK:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create or replace function hs_hosting_asset_booking_item_hierarchy_check_tf()
returns trigger
language plpgsql as $$
declare
actualBookingItemType HsBookingItemType;
expectedBookingItemTypes HsBookingItemType[];
begin
actualBookingItemType := (select type
from hs_booking_item
where NEW.bookingItemUuid = uuid);
if NEW.type = 'CLOUD_SERVER' then
expectedBookingItemTypes := ARRAY['PRIVATE_CLOUD', 'CLOUD_SERVER'];
elsif NEW.type = 'MANAGED_SERVER' then
expectedBookingItemTypes := ARRAY['PRIVATE_CLOUD', 'MANAGED_SERVER'];
elsif NEW.type = 'MANAGED_WEBSPACE' then
if NEW.parentAssetUuid is null then
expectedBookingItemTypes := ARRAY['MANAGED_WEBSPACE'];
else
expectedBookingItemTypes := ARRAY['PRIVATE_CLOUD', 'MANAGED_SERVER'];
end if;
end if;
if not actualBookingItemType = any(expectedBookingItemTypes) then
raise exception '[400] % % must have any of % as booking-item, but got %',
NEW.type, NEW.identifier, expectedBookingItemTypes, actualBookingItemType;
end if;
return NEW;
end; $$;
create trigger hs_hosting_asset_booking_item_hierarchy_check_tg
before insert on hs_hosting_asset
for each row
execute procedure hs_hosting_asset_booking_item_hierarchy_check_tf();
--//
-- ============================================================================
--changeset hs-hosting-asset-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
-- ----------------------------------------------------------------------------

View File

@ -15,10 +15,11 @@ create or replace procedure createHsHostingAssetTestData(
)
language plpgsql as $$
declare
currentTask varchar;
relatedDebitor hs_office_debitor;
relatedBookingItem hs_booking_item;
managedServerUuid uuid;
currentTask varchar;
relatedDebitor hs_office_debitor;
relatedPrivateCloudBookingItem hs_booking_item;
relatedManagedServerBookingItem hs_booking_item;
managedServerUuid uuid;
begin
currentTask := 'creating hosting-asset test-data ' || givenPartnerNumber::text || givenDebitorSuffix;
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
@ -30,19 +31,23 @@ begin
join hs_office_relation partnerRel on partnerRel.holderUuid = debitorRel.anchorUuid
join hs_office_partner partner on partner.partnerRelUuid = partnerRel.uuid
where partner.partnerNumber = givenPartnerNumber and debitor.debitorNumberSuffix = givenDebitorSuffix;
select item.* into relatedBookingItem
select item.uuid into relatedPrivateCloudBookingItem
from hs_booking_item item
where item.debitoruuid = relatedDebitor.uuid
and item.caption = 'some PrivateCloud';
and item.type = 'PRIVATE_CLOUD';
select item.uuid into relatedManagedServerBookingItem
from hs_booking_item item
where item.debitoruuid = relatedDebitor.uuid
and item.type = 'MANAGED_SERVER';
select uuid_generate_v4() into managedServerUuid;
raise notice 'creating test hosting-asset: %', givenPartnerNumber::text || givenDebitorSuffix::text;
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
insert
into hs_hosting_asset (uuid, bookingitemuuid, type, parentAssetUuid, identifier, caption, config)
values (managedServerUuid, relatedBookingItem.uuid, 'MANAGED_SERVER', null, 'vm10' || givenDebitorSuffix, 'some ManagedServer', '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedBookingItem.uuid, 'CLOUD_SERVER', null, 'vm20' || givenDebitorSuffix, 'another CloudServer', '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedBookingItem.uuid, 'MANAGED_WEBSPACE', managedServerUuid, givenWebspacePrefix || '01', 'some Webspace', '{ "RAM": 1, "SDD": 512, "HDD": 2048, "extra": 42 }'::jsonb);
insert into hs_hosting_asset
(uuid, bookingitemuuid, type, parentAssetUuid, identifier, caption, config)
values (managedServerUuid, relatedPrivateCloudBookingItem.uuid, 'MANAGED_SERVER', null, 'vm10' || givenDebitorSuffix, 'some ManagedServer', '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedPrivateCloudBookingItem.uuid, 'CLOUD_SERVER', null, 'vm20' || givenDebitorSuffix, 'another CloudServer', '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
(uuid_generate_v4(), relatedManagedServerBookingItem.uuid, 'MANAGED_WEBSPACE', managedServerUuid, givenWebspacePrefix || '01', 'some Webspace', '{ "RAM": 1, "SDD": 512, "HDD": 2048, "extra": 42 }'::jsonb);
end; $$;
--//
@ -58,3 +63,4 @@ do language plpgsql $$
call createHsHostingAssetTestData(10003, '13', 'ccc');
end;
$$;
--//

View File

@ -121,6 +121,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
.body("""
{
"debitorUuid": "%s",
"type": "MANAGED_SERVER",
"caption": "some new booking",
"resources": { "CPU": 12, "extra": 42 },
"validFrom": "2022-10-13"
@ -134,6 +135,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
.contentType(ContentType.JSON)
.body("", lenientlyEquals("""
{
"type": "MANAGED_SERVER",
"caption": "some new booking",
"validFrom": "2022-10-13",
"validTo": null,
@ -328,6 +330,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
final var newBookingItem = HsBookingItemEntity.builder()
.uuid(UUID.randomUUID())
.debitor(givenDebitor)
.type(HsBookingItemType.MANAGED_WEBSPACE)
.caption("some test-booking")
.resources(Map.ofEntries(resources))
.validity(Range.closedOpen(

View File

@ -16,6 +16,7 @@ class HsBookingItemEntityUnitTest {
final HsBookingItemEntity givenBookingItem = HsBookingItemEntity.builder()
.debitor(TEST_DEBITOR)
.type(HsBookingItemType.CLOUD_SERVER)
.caption("some caption")
.resources(Map.ofEntries(
entry("CPUs", 2),
@ -28,7 +29,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, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })");
}
@Test

View File

@ -25,6 +25,8 @@ import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
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;
@ -70,6 +72,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
final var result = attempt(em, () -> {
final var newBookingItem = HsBookingItemEntity.builder()
.debitor(givenDebitor)
.type(HsBookingItemType.CLOUD_SERVER)
.caption("some new booking item")
.validity(Range.closedOpen(
LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01")))
@ -98,6 +101,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
final var newBookingItem = HsBookingItemEntity.builder()
.debitor(givenDebitor)
.type(MANAGED_WEBSPACE)
.caption("some new booking item")
.validity(Range.closedOpen(
LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01")))
@ -163,9 +167,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
// then
allTheseBookingItemsAreReturned(
result,
"HsBookingItemEntity(D-1000212, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
"HsBookingItemEntity(D-1000212, [2023-01-15,2024-04-15), some CloudServer, { CPU: 2, HDD: 1024, extra: 42 })",
"HsBookingItemEntity(D-1000212, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10240, SDD: 10240, extra: 42 })");
"HsBookingItemEntity(D-1000212, MANAGED_SERVER, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
"HsBookingItemEntity(D-1000212, CLOUD_SERVER, [2023-01-15,2024-04-15), some CloudServer, { CPU: 2, HDD: 1024, extra: 42 })",
"HsBookingItemEntity(D-1000212, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10240, SDD: 10240, extra: 42 })");
}
@Test
@ -180,9 +184,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
// then:
exactlyTheseBookingItemsAreReturned(
result,
"HsBookingItemEntity(D-1000111, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
"HsBookingItemEntity(D-1000111, [2023-01-15,2024-04-15), some CloudServer, { CPU: 2, HDD: 1024, extra: 42 })",
"HsBookingItemEntity(D-1000111, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10240, SDD: 10240, extra: 42 })");
"HsBookingItemEntity(D-1000111, MANAGED_SERVER, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
"HsBookingItemEntity(D-1000111, CLOUD_SERVER, [2023-01-15,2024-04-15), some CloudServer, { CPU: 2, HDD: 1024, extra: 42 })",
"HsBookingItemEntity(D-1000111, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10240, SDD: 10240, extra: 42 })");
}
}
@ -315,6 +319,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
final var givenDebitor = debitorRepo.findDebitorByDebitorNumber(debitorNumber).get(0);
final var newBookingItem = HsBookingItemEntity.builder()
.debitor(givenDebitor)
.type(MANAGED_SERVER)
.caption("some temp booking item")
.validity(Range.closedOpen(
LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01")))

View File

@ -26,6 +26,7 @@ import java.util.Map;
import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
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;
@ -68,7 +69,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
// given
context("superuser-alex@hostsharing.net");
final var count = assetRepo.count();
final var givenManagedServer = givenManagedServer("First", "some ManagedServer");
final var givenManagedServer = givenManagedServer("First", MANAGED_SERVER);
// when
final var result = attempt(em, () -> {
@ -162,7 +163,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
// then
allTheseServersAreReturned(
result,
"HsHostingAssetEntity(D-1000212:some PrivateCloud, MANAGED_WEBSPACE, D-1000212:some PrivateCloud:vm1012, bbb01, some Webspace, { HDD: 2048, RAM: 1, SDD: 512, extra: 42 })",
"HsHostingAssetEntity(D-1000212:some ManagedServer, MANAGED_WEBSPACE, D-1000212:some PrivateCloud:vm1012, bbb01, some Webspace, { HDD: 2048, RAM: 1, SDD: 512, extra: 42 })",
"HsHostingAssetEntity(D-1000212:some PrivateCloud, MANAGED_SERVER, vm1012, some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
"HsHostingAssetEntity(D-1000212:some PrivateCloud, CLOUD_SERVER, vm2012, another CloudServer, { CPU: 2, HDD: 1024, extra: 42 })");
}
@ -179,7 +180,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
// then:
exactlyTheseAssetsAreReturned(
result,
"HsHostingAssetEntity(D-1000111:some PrivateCloud, MANAGED_WEBSPACE, D-1000111:some PrivateCloud:vm1011, aaa01, some Webspace, { HDD: 2048, RAM: 1, SDD: 512, extra: 42 })",
"HsHostingAssetEntity(D-1000111:some ManagedServer, MANAGED_WEBSPACE, D-1000111:some PrivateCloud:vm1011, aaa01, some Webspace, { HDD: 2048, RAM: 1, SDD: 512, extra: 42 })",
"HsHostingAssetEntity(D-1000111:some PrivateCloud, MANAGED_SERVER, vm1011, some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
"HsHostingAssetEntity(D-1000111:some PrivateCloud, CLOUD_SERVER, vm2011, another CloudServer, { CPU: 2, HDD: 1024, extra: 42 })");
}
@ -353,10 +354,10 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
.findAny().orElseThrow();
}
HsHostingAssetEntity givenManagedServer(final String debitorName, final String hostingAssetCaption) {
HsHostingAssetEntity givenManagedServer(final String debitorName, final HsHostingAssetType type) {
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike(debitorName).stream().findAny().orElseThrow();
return assetRepo.findAllByDebitorUuid(givenDebitor.getUuid()).stream()
.filter(i -> i.getCaption().equals(hostingAssetCaption))
.filter(i -> i.getType().equals(type))
.findAny().orElseThrow();
}