rename partnerRole -> partnerRel, relationship -> relation and remove rel-Prefix (relAnchor etc.) #23

Merged
hsh-michaelhoennig merged 5 commits from rename-partnerRole-to-partnerRel into master 2024-03-13 15:01:25 +01:00
39 changed files with 873 additions and 914 deletions

View File

@ -10,7 +10,7 @@ classDiagram
namespace Partner { namespace Partner {
class partner-MeierGmbH class partner-MeierGmbH
class role-MeierGmbH class rel-MeierGmbH
class personDetails-MeierGmbH class personDetails-MeierGmbH
class contactData-MeierGmbH class contactData-MeierGmbH
class person-MeierGmbH class person-MeierGmbH
@ -19,28 +19,29 @@ classDiagram
namespace Representatives { namespace Representatives {
class person-FrankMeier class person-FrankMeier
class contactData-FrankMeier class contactData-FrankMeier
class role-MeierGmbH-FrankMeier class rel-MeierGmbH-FrankMeier
} }
namespace Debitors { namespace Debitors {
class debitor-MeierGmbH class debitor-MeierGmbH
class contactData-MeierGmbH-Buha class contactData-MeierGmbH-Buha
class role-MeierGmbH-Buha class rel-MeierGmbH-Buha
} }
namespace Operations { namespace Operations {
class person-SabineMeier class person-SabineMeier
class contactData-SabineMeier class contactData-SabineMeier
class role-MeierGmbH-SabineMeier class rel-MeierGmbH-SabineMeier
} }
namespace Enums { namespace Enums {
class RoleType { class RelationType {
<<enumeration>> <<enumeration>>
UNKNOWN UNKNOWN
PARTNER
DEBITOR
REPRESENTATIVE REPRESENTATIVE
ACCOUNTING
OPERATIONS OPERATIONS
} }
@ -64,9 +65,9 @@ classDiagram
class partner-MeierGmbH { class partner-MeierGmbH {
+Numeric partnerNumber: 12345 +Numeric partnerNumber: 12345
+Role partnerRole +Relation partnerRel
} }
partner-MeierGmbH *-- role-MeierGmbH partner-MeierGmbH *-- rel-MeierGmbH
class person-MeierGmbH { class person-MeierGmbH {
+personType: LEGAL +personType: LEGAL
@ -90,22 +91,22 @@ classDiagram
+emailAddresses: office@meier-gmbh.de +emailAddresses: office@meier-gmbh.de
} }
class role-MeierGmbH { class rel-MeierGmbH {
+RoleType RoleType PARTNER +RelationType type PARTNER
+Person anchor +Person anchor
+Person holder +Person holder
+Contact roleContact +Contact contact
} }
role-MeierGmbH o-- person-HostsharingEG : anchor rel-MeierGmbH o-- person-HostsharingEG : anchor
role-MeierGmbH o-- person-MeierGmbH : holder rel-MeierGmbH o-- person-MeierGmbH : holder
role-MeierGmbH o-- contactData-MeierGmbH rel-MeierGmbH o-- contactData-MeierGmbH
%% --- Debitors --- %% --- Debitors ---
class debitor-MeierGmbH { class debitor-MeierGmbH {
+Partner partner +Partner partner
+Numeric[2] debitorNumberSuffix: 00 +Numeric[2] debitorNumberSuffix: 00
+Role billingRole +Relation debitorRel
+boolean billable: true +boolean billable: true
+String vatId: ID123456789 +String vatId: ID123456789
+String vatCountryCode: DE +String vatCountryCode: DE
@ -115,7 +116,7 @@ classDiagram
+String defaultPrefix: mei +String defaultPrefix: mei
} }
debitor-MeierGmbH o-- partner-MeierGmbH debitor-MeierGmbH o-- partner-MeierGmbH
debitor-MeierGmbH *-- role-MeierGmbH-Buha debitor-MeierGmbH *-- rel-MeierGmbH-Buha
class contactData-MeierGmbH-Buha { class contactData-MeierGmbH-Buha {
+postalAddress: Hauptstraße 5, 22345 Hamburg +postalAddress: Hauptstraße 5, 22345 Hamburg
@ -123,15 +124,15 @@ classDiagram
+emailAddresses: buha@meier-gmbh.de +emailAddresses: buha@meier-gmbh.de
} }
class role-MeierGmbH-Buha { class rel-MeierGmbH-Buha {
+RoleType RoleType ACCOUNTING +RelationType type DEBITOR
+Person anchor +Person anchor
+Person holder +Person holder
+Contact roleContact +Contact contact
} }
role-MeierGmbH-Buha o-- person-MeierGmbH : anchor rel-MeierGmbH-Buha o-- person-MeierGmbH : anchor
role-MeierGmbH-Buha o-- person-MeierGmbH : holder rel-MeierGmbH-Buha o-- person-MeierGmbH : holder
role-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha rel-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha
%% --- Representatives --- %% --- Representatives ---
@ -148,15 +149,15 @@ classDiagram
+emailAddresses: frank.meier@meier-gmbh.de +emailAddresses: frank.meier@meier-gmbh.de
} }
class role-MeierGmbH-FrankMeier { class rel-MeierGmbH-FrankMeier {
+RoleType RoleType REPRESENTATIVE +RelationType type REPRESENTATIVE
+Person anchor +Person anchor
+Person holder +Person holder
+Contact roleContact +Contact contact
} }
role-MeierGmbH-FrankMeier o-- person-MeierGmbH : anchor rel-MeierGmbH-FrankMeier o-- person-MeierGmbH : anchor
role-MeierGmbH-FrankMeier o-- person-FrankMeier : holder rel-MeierGmbH-FrankMeier o-- person-FrankMeier : holder
role-MeierGmbH-FrankMeier o-- contactData-FrankMeier rel-MeierGmbH-FrankMeier o-- contactData-FrankMeier
%% --- Operations --- %% --- Operations ---
@ -173,14 +174,14 @@ classDiagram
+emailAddresses: sabine.meier@meier-gmbh.de +emailAddresses: sabine.meier@meier-gmbh.de
} }
class role-MeierGmbH-SabineMeier { class rel-MeierGmbH-SabineMeier {
+RoleType RoleType OPERATIONAL +RelationType type OPERATIONAL
+Person anchor +Person anchor
+Person holder +Person holder
+Contact roleContact +Contact contact
} }
role-MeierGmbH-SabineMeier o-- person-MeierGmbH : anchor rel-MeierGmbH-SabineMeier o-- person-MeierGmbH : anchor
role-MeierGmbH-SabineMeier o-- person-SabineMeier : holder rel-MeierGmbH-SabineMeier o-- person-SabineMeier : holder
role-MeierGmbH-SabineMeier o-- contactData-SabineMeier rel-MeierGmbH-SabineMeier o-- contactData-SabineMeier
``` ```

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@ -113,10 +113,10 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
SELECT debitor.uuid, SELECT debitor.uuid,
'D-' || (SELECT partner.partnerNumber 'D-' || (SELECT partner.partnerNumber
FROM hs_office_partner partner FROM hs_office_partner partner
JOIN hs_office_relationship partnerRel JOIN hs_office_relation partnerRel
ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER' ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER'
JOIN hs_office_relationship debitorRel JOIN hs_office_relation debitorRel
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'DEBITOR' ON debitorRel.anchorUuid = partnerRel.holderUuid AND partnerRel.type = 'DEBITOR'
WHERE debitorRel.uuid = debitor.debitorRelUuid) WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00') || to_char(debitorNumberSuffix, 'fm00')
from hs_office_debitor as debitor from hs_office_debitor as debitor
@ -133,11 +133,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
"defaultPrefix" /* TODO: do we want that updatable? */) "defaultPrefix" /* TODO: do we want that updatable? */)
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN) .createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class, .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
fetchedBySql(""" fetchedBySql("""
SELECT * SELECT *
FROM hs_office_relationship AS r FROM hs_office_relation AS r
WHERE r.relType = 'DEBITOR' AND r.relHolderUuid = ${REF}.debitorRelUuid WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
"""), """),
dependsOnColumn("debitorRelUuid")) dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER) .createPermission(DELETE).grantedTo("debitorRel", OWNER)
@ -147,18 +147,18 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, .importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
dependsOnColumn("refundBankAccountUuid"), fetchedBySql(""" dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
SELECT * SELECT *
FROM hs_office_relationship AS r FROM hs_office_relation AS r
WHERE r.relType = 'DEBITOR' AND r.relHolderUuid = ${REF}.debitorRelUuid WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
""") """)
) )
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class, .importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("partnerRelUuid"), fetchedBySql(""" dependsOnColumn("partnerRelUuid"), fetchedBySql("""
SELECT * SELECT *
FROM hs_office_relationship AS partnerRel FROM hs_office_relation AS partnerRel
WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
""") """)
) )
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)

View File

@ -7,11 +7,11 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartners
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRoleInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRelInsertResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -40,7 +40,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerRepository partnerRepo; private HsOfficePartnerRepository partnerRepo;
@Autowired @Autowired
private HsOfficeRelationshipRepository relationshipRepo; private HsOfficeRelationRepository relationRepo;
@PersistenceContext @PersistenceContext
private EntityManager em; private EntityManager em;
@ -112,7 +112,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
if (partnerRepo.deleteByUuid(partnerUuid) != 1 || if (partnerRepo.deleteByUuid(partnerUuid) != 1 ||
// TODO: move to after delete trigger in partner // TODO: move to after delete trigger in partner
relationshipRepo.deleteByUuid(partnerToDelete.get().getPartnerRole().getUuid()) != 1 ) { relationRepo.deleteByUuid(partnerToDelete.get().getPartnerRel().getUuid()) != 1 ) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
} }
@ -141,18 +141,18 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) { private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
final var entityToSave = new HsOfficePartnerEntity(); final var entityToSave = new HsOfficePartnerEntity();
entityToSave.setPartnerNumber(body.getPartnerNumber()); entityToSave.setPartnerNumber(body.getPartnerNumber());
entityToSave.setPartnerRole(persistPartnerRole(body.getPartnerRole())); entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
entityToSave.setContact(ref(HsOfficeContactEntity.class, body.getContactUuid())); entityToSave.setContact(ref(HsOfficeContactEntity.class, body.getContactUuid()));
entityToSave.setPerson(ref(HsOfficePersonEntity.class, body.getPersonUuid())); entityToSave.setPerson(ref(HsOfficePersonEntity.class, body.getPersonUuid()));
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class)); entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
return entityToSave; return entityToSave;
} }
private HsOfficeRelationshipEntity persistPartnerRole(final HsOfficePartnerRoleInsertResource resource) { private HsOfficeRelationEntity persistPartnerRel(final HsOfficePartnerRelInsertResource resource) {
final var entity = new HsOfficeRelationshipEntity(); final var entity = new HsOfficeRelationEntity();
entity.setRelType(HsOfficeRelationshipType.PARTNER); entity.setType(HsOfficeRelationType.PARTNER);
entity.setRelAnchor(ref(HsOfficePersonEntity.class, resource.getRelAnchorUuid())); entity.setAnchor(ref(HsOfficePersonEntity.class, resource.getAnchorUuid()));
entity.setRelHolder(ref(HsOfficePersonEntity.class, resource.getRelHolderUuid())); entity.setHolder(ref(HsOfficePersonEntity.class, resource.getHolderUuid()));
entity.setContact(ref(HsOfficeContactEntity.class, resource.getContactUuid())); entity.setContact(ref(HsOfficeContactEntity.class, resource.getContactUuid()));
em.persist(entity); em.persist(entity);
return entity; return entity;

View File

@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.hs.office.partner;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@ -86,15 +86,15 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
"dateOfDeath") "dateOfDeath")
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN) .createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
fetchedBySql(""" fetchedBySql("""
SELECT partnerRel.* SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel FROM hs_office_relation AS partnerRel
JOIN hs_office_partner AS partner JOIN hs_office_partner AS partner
ON partner.detailsUuid = ${ref}.uuid ON partner.detailsUuid = ${ref}.uuid
WHERE partnerRel.uuid = partner.partnerRoleUuid WHERE partnerRel.uuid = partner.partnerRelUuid
"""), """),
dependsOnColumn("partnerRoleUuid")) dependsOnColumn("partnerRelUuid"))
// The grants are defined in HsOfficePartnerEntity.rbac() // The grants are defined in HsOfficePartnerEntity.rbac()
// because they have to be changed when its partnerRel changes, // because they have to be changed when its partnerRel changes,

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
@ -50,15 +50,15 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
private Integer partnerNumber; private Integer partnerNumber;
@ManyToOne @ManyToOne
@JoinColumn(name = "partnerroleuuid", nullable = false) @JoinColumn(name = "partnerreluuid", nullable = false)
private HsOfficeRelationshipEntity partnerRole; private HsOfficeRelationEntity partnerRel;
// TODO: remove, is replaced by partnerRole // TODO: remove, is replaced by partnerRel
@ManyToOne @ManyToOne
@JoinColumn(name = "personuuid", nullable = false) @JoinColumn(name = "personuuid", nullable = false)
private HsOfficePersonEntity person; private HsOfficePersonEntity person;
// TODO: remove, is replaced by partnerRole // TODO: remove, is replaced by partnerRel
@ManyToOne @ManyToOne
@JoinColumn(name = "contactuuid", nullable = false) @JoinColumn(name = "contactuuid", nullable = false)
private HsOfficeContactEntity contact; private HsOfficeContactEntity contact;
@ -87,13 +87,13 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
FROM hs_office_partner AS partner FROM hs_office_partner AS partner
""")) """))
.withUpdatableColumns( .withUpdatableColumns(
"partnerRoleUuid", "partnerRelUuid",
"personUuid", "personUuid",
"contactUuid") "contactUuid")
.createPermission(custom("new-partner")).grantedTo("global", ADMIN) .createPermission(custom("new-partner")).grantedTo("global", ADMIN)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"), fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
dependsOnColumn("partnerRelUuid")) dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN) .createPermission(DELETE).grantedTo("partnerRel", ADMIN)
.createPermission(UPDATE).grantedTo("partnerRel", AGENT) .createPermission(UPDATE).grantedTo("partnerRel", AGENT)

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationshipsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
@ -22,7 +22,7 @@ import java.util.function.BiConsumer;
@RestController @RestController
public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi { public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Autowired @Autowired
private Context context; private Context context;
@ -31,10 +31,10 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi
private Mapper mapper; private Mapper mapper;
@Autowired @Autowired
private HsOfficeRelationshipRepository relationshipRepo; private HsOfficeRelationRepository relationRepo;
@Autowired @Autowired
private HsOfficePersonRepository relHolderRepo; private HsOfficePersonRepository holderRepo;
@Autowired @Autowired
private HsOfficeContactRepository contactRepo; private HsOfficeContactRepository contactRepo;
@ -44,79 +44,79 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeRelationshipResource>> listRelationships( public ResponseEntity<List<HsOfficeRelationResource>> listRelations(
final String currentUser, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID personUuid, final UUID personUuid,
final HsOfficeRelationshipTypeResource relationshipType) { final HsOfficeRelationTypeResource relationType) {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = relationshipRepo.findRelationshipRelatedToPersonUuidAndRelationshipType(personUuid, final var entities = relationRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
mapper.map(relationshipType, HsOfficeRelationshipType.class)); mapper.map(relationType, HsOfficeRelationType.class));
final var resources = mapper.mapList(entities, HsOfficeRelationshipResource.class, final var resources = mapper.mapList(entities, HsOfficeRelationResource.class,
RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER); RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(resources); return ResponseEntity.ok(resources);
} }
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeRelationshipResource> addRelationship( public ResponseEntity<HsOfficeRelationResource> addRelation(
final String currentUser, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeRelationshipInsertResource body) { final HsOfficeRelationInsertResource body) {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = new HsOfficeRelationshipEntity(); final var entityToSave = new HsOfficeRelationEntity();
entityToSave.setRelType(HsOfficeRelationshipType.valueOf(body.getRelType())); entityToSave.setType(HsOfficeRelationType.valueOf(body.getType()));
entityToSave.setRelAnchor(relHolderRepo.findByUuid(body.getRelAnchorUuid()).orElseThrow( entityToSave.setAnchor(holderRepo.findByUuid(body.getAnchorUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find relAnchorUuid " + body.getRelAnchorUuid()) () -> new NoSuchElementException("cannot find anchorUuid " + body.getAnchorUuid())
)); ));
entityToSave.setRelHolder(relHolderRepo.findByUuid(body.getRelHolderUuid()).orElseThrow( entityToSave.setHolder(holderRepo.findByUuid(body.getHolderUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find relHolderUuid " + body.getRelHolderUuid()) () -> new NoSuchElementException("cannot find holderUuid " + body.getHolderUuid())
)); ));
entityToSave.setContact(contactRepo.findByUuid(body.getContactUuid()).orElseThrow( entityToSave.setContact(contactRepo.findByUuid(body.getContactUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find contactUuid " + body.getContactUuid()) () -> new NoSuchElementException("cannot find contactUuid " + body.getContactUuid())
)); ));
final var saved = relationshipRepo.save(entityToSave); final var saved = relationRepo.save(entityToSave);
final var uri = final var uri =
MvcUriComponentsBuilder.fromController(getClass()) MvcUriComponentsBuilder.fromController(getClass())
.path("/api/hs/office/relationships/{id}") .path("/api/hs/office/relations/{id}")
.buildAndExpand(saved.getUuid()) .buildAndExpand(saved.getUuid())
.toUri(); .toUri();
final var mapped = mapper.map(saved, HsOfficeRelationshipResource.class, final var mapped = mapper.map(saved, HsOfficeRelationResource.class,
RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER); RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.created(uri).body(mapped); return ResponseEntity.created(uri).body(mapped);
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeRelationshipResource> getRelationshipByUuid( public ResponseEntity<HsOfficeRelationResource> getRelationByUuid(
final String currentUser, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID relationshipUuid) { final UUID relationUuid) {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var result = relationshipRepo.findByUuid(relationshipUuid); final var result = relationRepo.findByUuid(relationUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeRelationshipResource.class, RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER)); return ResponseEntity.ok(mapper.map(result.get(), HsOfficeRelationResource.class, RELATION_ENTITY_TO_RESOURCE_POSTMAPPER));
} }
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteRelationshipByUuid( public ResponseEntity<Void> deleteRelationByUuid(
final String currentUser, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID relationshipUuid) { final UUID relationUuid) {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var result = relationshipRepo.deleteByUuid(relationshipUuid); final var result = relationRepo.deleteByUuid(relationUuid);
if (result == 0) { if (result == 0) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
@ -126,27 +126,27 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeRelationshipResource> patchRelationship( public ResponseEntity<HsOfficeRelationResource> patchRelation(
final String currentUser, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID relationshipUuid, final UUID relationUuid,
final HsOfficeRelationshipPatchResource body) { final HsOfficeRelationPatchResource body) {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var current = relationshipRepo.findByUuid(relationshipUuid).orElseThrow(); final var current = relationRepo.findByUuid(relationUuid).orElseThrow();
new HsOfficeRelationshipEntityPatcher(em, current).apply(body); new HsOfficeRelationEntityPatcher(em, current).apply(body);
final var saved = relationshipRepo.save(current); final var saved = relationRepo.save(current);
final var mapped = mapper.map(saved, HsOfficeRelationshipResource.class); final var mapped = mapper.map(saved, HsOfficeRelationResource.class);
return ResponseEntity.ok(mapped); return ResponseEntity.ok(mapped);
} }
final BiConsumer<HsOfficeRelationshipEntity, HsOfficeRelationshipResource> RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { final BiConsumer<HsOfficeRelationEntity, HsOfficeRelationResource> RELATION_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setRelAnchor(mapper.map(entity.getRelAnchor(), HsOfficePersonResource.class)); resource.setAnchor(mapper.map(entity.getAnchor(), HsOfficePersonResource.class));
resource.setRelHolder(mapper.map(entity.getRelHolder(), HsOfficePersonResource.class)); resource.setHolder(mapper.map(entity.getHolder(), HsOfficePersonResource.class));
resource.setContact(mapper.map(entity.getContact(), HsOfficeContactResource.class)); resource.setContact(mapper.map(entity.getContact(), HsOfficeContactResource.class));
}; };
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
import lombok.*; import lombok.*;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
@ -24,49 +24,49 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(name = "hs_office_relationship_rv") @Table(name = "hs_office_relation_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@FieldNameConstants @FieldNameConstants
public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable { public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
private static Stringify<HsOfficeRelationshipEntity> toString = stringify(HsOfficeRelationshipEntity.class, "rel") private static Stringify<HsOfficeRelationEntity> toString = stringify(HsOfficeRelationEntity.class, "rel")
.withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor) .withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
.withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType) .withProp(Fields.type, HsOfficeRelationEntity::getType)
.withProp(Fields.relMark, HsOfficeRelationshipEntity::getRelMark) .withProp(Fields.mark, HsOfficeRelationEntity::getMark)
.withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder) .withProp(Fields.holder, HsOfficeRelationEntity::getHolder)
.withProp(Fields.contact, HsOfficeRelationshipEntity::getContact); .withProp(Fields.contact, HsOfficeRelationEntity::getContact);
private static Stringify<HsOfficeRelationshipEntity> toShortString = stringify(HsOfficeRelationshipEntity.class, "rel") private static Stringify<HsOfficeRelationEntity> toShortString = stringify(HsOfficeRelationEntity.class, "rel")
.withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor) .withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
.withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType) .withProp(Fields.type, HsOfficeRelationEntity::getType)
.withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder); .withProp(Fields.holder, HsOfficeRelationEntity::getHolder);
@Id @Id
@GeneratedValue @GeneratedValue
private UUID uuid; private UUID uuid;
@ManyToOne @ManyToOne
@JoinColumn(name = "relanchoruuid") @JoinColumn(name = "anchoruuid")
private HsOfficePersonEntity relAnchor; private HsOfficePersonEntity anchor;
@ManyToOne @ManyToOne
@JoinColumn(name = "relholderuuid") @JoinColumn(name = "holderuuid")
private HsOfficePersonEntity relHolder; private HsOfficePersonEntity holder;
@ManyToOne @ManyToOne
@JoinColumn(name = "contactuuid") @JoinColumn(name = "contactuuid")
private HsOfficeContactEntity contact; private HsOfficeContactEntity contact;
@Column(name = "reltype") @Column(name = "type")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private HsOfficeRelationshipType relType; private HsOfficeRelationType type;
@Column(name = "relmark") @Column(name = "mark")
private String relMark; private String mark;
@Override @Override
public String toString() { public String toString() {
@ -79,22 +79,22 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
} }
public static RbacView rbac() { public static RbacView rbac() {
return rbacViewFor("relationship", HsOfficeRelationshipEntity.class) return rbacViewFor("relation", HsOfficeRelationEntity.class)
.withIdentityView(SQL.projection(""" .withIdentityView(SQL.projection("""
(select idName from hs_office_person_iv p where p.uuid = relAnchorUuid) (select idName from hs_office_person_iv p where p.uuid = anchorUuid)
|| '-with-' || target.relType || '-' || '-with-' || target.type || '-'
|| (select idName from hs_office_person_iv p where p.uuid = relHolderUuid) || (select idName from hs_office_person_iv p where p.uuid = holderUuid)
""")) """))
.withRestrictedViewOrderBy(SQL.expression( .withRestrictedViewOrderBy(SQL.expression(
"(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)")) "(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)"))
.withUpdatableColumns("contactUuid") .withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class, .importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
dependsOnColumn("relAnchorUuid"), dependsOnColumn("anchorUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid") fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid")
) )
.importEntityAlias("holderPerson", HsOfficePersonEntity.class, .importEntityAlias("holderPerson", HsOfficePersonEntity.class,
dependsOnColumn("relHolderUuid"), dependsOnColumn("holderUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid") fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid")
) )
.importEntityAlias("contact", HsOfficeContactEntity.class, .importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"), dependsOnColumn("contactUuid"),
@ -123,6 +123,6 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("223-hs-office-relationship-rbac-generated"); rbac().generateWithBaseFileName("223-hs-office-relation-rbac-generated");
} }
} }

View File

@ -1,25 +1,25 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationshipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationPatchResource;
import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson; import net.hostsharing.hsadminng.mapper.OptionalFromJson;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import java.util.UUID; import java.util.UUID;
class HsOfficeRelationshipEntityPatcher implements EntityPatcher<HsOfficeRelationshipPatchResource> { class HsOfficeRelationEntityPatcher implements EntityPatcher<HsOfficeRelationPatchResource> {
private final EntityManager em; private final EntityManager em;
private final HsOfficeRelationshipEntity entity; private final HsOfficeRelationEntity entity;
HsOfficeRelationshipEntityPatcher(final EntityManager em, final HsOfficeRelationshipEntity entity) { HsOfficeRelationEntityPatcher(final EntityManager em, final HsOfficeRelationEntity entity) {
this.em = em; this.em = em;
this.entity = entity; this.entity = entity;
} }
@Override @Override
public void apply(final HsOfficeRelationshipPatchResource resource) { public void apply(final HsOfficeRelationPatchResource resource) {
OptionalFromJson.of(resource.getContactUuid()).ifPresent(newValue -> { OptionalFromJson.of(resource.getContactUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "contact"); verifyNotNull(newValue, "contact");
entity.setContact(em.getReference(HsOfficeContactEntity.class, newValue)); entity.setContact(em.getReference(HsOfficeContactEntity.class, newValue));

View File

@ -0,0 +1,37 @@
package net.hostsharing.hsadminng.hs.office.relation;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface HsOfficeRelationRepository extends Repository<HsOfficeRelationEntity, UUID> {
Optional<HsOfficeRelationEntity> findByUuid(UUID id);
default List<HsOfficeRelationEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType.toString());
}
@Query(value = """
SELECT p.* FROM hs_office_relation_rv AS p
WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
""", nativeQuery = true)
List<HsOfficeRelationEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
@Query(value = """
SELECT p.* FROM hs_office_relation_rv AS p
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType))
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
""", nativeQuery = true)
List<HsOfficeRelationEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
HsOfficeRelationEntity save(final HsOfficeRelationEntity entity);
long count();
int deleteByUuid(UUID uuid);
}

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
public enum HsOfficeRelationshipType { public enum HsOfficeRelationType {
UNKNOWN, UNKNOWN,
PARTNER, PARTNER,
EX_PARTNER, EX_PARTNER,

View File

@ -1,37 +0,0 @@
package net.hostsharing.hsadminng.hs.office.relationship;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface HsOfficeRelationshipRepository extends Repository<HsOfficeRelationshipEntity, UUID> {
Optional<HsOfficeRelationshipEntity> findByUuid(UUID id);
default List<HsOfficeRelationshipEntity> findRelationshipRelatedToPersonUuidAndRelationshipType(@NotNull UUID personUuid, HsOfficeRelationshipType relationshipType) {
return findRelationshipRelatedToPersonUuidAndRelationshipTypeString(personUuid, relationshipType.toString());
}
@Query(value = """
SELECT p.* FROM hs_office_relationship_rv AS p
WHERE p.relAnchorUuid = :personUuid OR p.relHolderUuid = :personUuid
""", nativeQuery = true)
List<HsOfficeRelationshipEntity> findRelationshipRelatedToPersonUuid(@NotNull UUID personUuid);
@Query(value = """
SELECT p.* FROM hs_office_relationship_rv AS p
WHERE (:relationshipType IS NULL OR p.relType = cast(:relationshipType AS HsOfficeRelationshipType))
AND ( p.relAnchorUuid = :personUuid OR p.relHolderUuid = :personUuid)
""", nativeQuery = true)
List<HsOfficeRelationshipEntity> findRelationshipRelatedToPersonUuidAndRelationshipTypeString(@NotNull UUID personUuid, String relationshipType);
HsOfficeRelationshipEntity save(final HsOfficeRelationshipEntity entity);
long count();
int deleteByUuid(UUID uuid);
}

View File

@ -6,7 +6,7 @@ import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
@ -99,7 +99,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
.withIdentityView(projection("concat(tradeName, familyName, givenName)")) .withIdentityView(projection("concat(tradeName, familyName, givenName)"))
.withUpdatableColumns("reference", "agreement", "validity") .withUpdatableColumns("reference", "agreement", "validity")
.importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, dependsOnColumn("debitorRelUuid")) .importEntityAlias("debitorRel", HsOfficeRelationEntity.class, dependsOnColumn("debitorRelUuid"))
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid")) .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"))
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {

View File

@ -11,7 +11,7 @@ import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
@ -804,7 +804,7 @@ public class RbacView {
HsOfficePartnerDetailsEntity.class, HsOfficePartnerDetailsEntity.class,
HsOfficeBankAccountEntity.class, HsOfficeBankAccountEntity.class,
HsOfficeDebitorEntity.class, HsOfficeDebitorEntity.class,
HsOfficeRelationshipEntity.class, HsOfficeRelationEntity.class,
HsOfficeCoopAssetsTransactionEntity.class, HsOfficeCoopAssetsTransactionEntity.class,
HsOfficeContactEntity.class, HsOfficeContactEntity.class,
HsOfficeSepaMandateEntity.class, HsOfficeSepaMandateEntity.class,

View File

@ -23,7 +23,7 @@ map:
null: org.openapitools.jackson.nullable.JsonNullable null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/persons/{personUUID}: /api/hs/office/persons/{personUUID}:
null: org.openapitools.jackson.nullable.JsonNullable null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/relationships/{relationshipUUID}: /api/hs/office/relations/{relationUUID}:
null: org.openapitools.jackson.nullable.JsonNullable null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/bankaccounts/{bankAccountUUID}: /api/hs/office/bankaccounts/{bankAccountUUID}:
null: org.openapitools.jackson.nullable.JsonNullable null: org.openapitools.jackson.nullable.JsonNullable

View File

@ -96,8 +96,8 @@ components:
format: int8 format: int8
minimum: 10000 minimum: 10000
maximum: 99999 maximum: 99999
partnerRole: partnerRel:
$ref: '#/components/schemas/HsOfficePartnerRoleInsert' $ref: '#/components/schemas/HsOfficePartnerRelInsert'
personUuid: personUuid:
type: string type: string
format: uuid format: uuid
@ -112,22 +112,22 @@ components:
- contactUuid - contactUuid
- details - details
HsOfficePartnerRoleInsert: HsOfficePartnerRelInsert:
type: object type: object
nullable: false nullable: false
properties: properties:
relAnchorUuid: anchorUuid:
type: string type: string
format: uuid format: uuid
relHolderUuid: holderUuid:
type: string type: string
format: uuid format: uuid
contactUuid: contactUuid:
type: string type: string
format: uuid format: uuid
required: required:
- relAnchorUuid - anchorUuid
- relHolderUuid - holderUuid
- relContactUuid - relContactUuid
HsOfficePartnerDetailsInsert: HsOfficePartnerDetailsInsert:

View File

@ -3,37 +3,37 @@ components:
schemas: schemas:
HsOfficeRelationshipType: HsOfficeRelationType:
type: string type: string
enum: enum:
- UNKNOWN - UNKNOWN
- PARTNER - PARTNER
- EX_PARTNER - EX_PARTNER
- REPRESENTATIVE, - DEBITOR
- REPRESENTATIVE
- VIP_CONTACT - VIP_CONTACT
- ACCOUNTING,
- OPERATIONS - OPERATIONS
- SUBSCRIBER - SUBSCRIBER
HsOfficeRelationship: HsOfficeRelation:
type: object type: object
properties: properties:
uuid: uuid:
type: string type: string
format: uuid format: uuid
relAnchor: anchor:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson' $ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
relHolder: holder:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson' $ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
relType: type:
type: string type: string
relMark: mark:
type: string type: string
nullable: true nullable: true
contact: contact:
$ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact' $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
HsOfficeRelationshipPatch: HsOfficeRelationPatch:
type: object type: object
properties: properties:
contactUuid: contactUuid:
@ -41,25 +41,25 @@ components:
format: uuid format: uuid
nullable: true nullable: true
HsOfficeRelationshipInsert: HsOfficeRelationInsert:
type: object type: object
properties: properties:
relAnchorUuid: anchorUuid:
type: string type: string
format: uuid format: uuid
relHolderUuid: holderUuid:
type: string type: string
format: uuid format: uuid
relType: type:
type: string type: string
nullable: true nullable: true
relMark: mark:
type: string type: string
contactUuid: contactUuid:
type: string type: string
format: uuid format: uuid
required: required:
- relAnchorUuid - anchorUuid
- relHolderUuid - holderUuid
- relType - type
- relContactUuid - relContactUuid

View File

@ -1,25 +1,25 @@
get: get:
tags: tags:
- hs-office-relationships - hs-office-relations
description: 'Fetch a single person relationship by its uuid, if visible for the current subject.' description: 'Fetch a single person relation by its uuid, if visible for the current subject.'
operationId: getRelationshipByUuid operationId: getRelationByUuid
parameters: parameters:
- $ref: './auth.yaml#/components/parameters/currentUser' - $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles' - $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: relationshipUUID - name: relationUUID
in: path in: path
required: true required: true
schema: schema:
type: string type: string
format: uuid format: uuid
description: UUID of the relationship to fetch. description: UUID of the relation to fetch.
responses: responses:
"200": "200":
description: OK description: OK
content: content:
'application/json': 'application/json':
schema: schema:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401": "401":
$ref: './error-responses.yaml#/components/responses/Unauthorized' $ref: './error-responses.yaml#/components/responses/Unauthorized'
@ -28,13 +28,13 @@ get:
patch: patch:
tags: tags:
- hs-office-relationships - hs-office-relations
description: 'Updates a single person relationship by its uuid, if permitted for the current subject.' description: 'Updates a single person relation by its uuid, if permitted for the current subject.'
operationId: patchRelationship operationId: patchRelation
parameters: parameters:
- $ref: './auth.yaml#/components/parameters/currentUser' - $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles' - $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: relationshipUUID - name: relationUUID
in: path in: path
required: true required: true
schema: schema:
@ -44,14 +44,14 @@ patch:
content: content:
'application/json': 'application/json':
schema: schema:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipPatch' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
responses: responses:
"200": "200":
description: OK description: OK
content: content:
'application/json': 'application/json':
schema: schema:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401": "401":
$ref: './error-responses.yaml#/components/responses/Unauthorized' $ref: './error-responses.yaml#/components/responses/Unauthorized'
"403": "403":
@ -59,19 +59,19 @@ patch:
delete: delete:
tags: tags:
- hs-office-relationships - hs-office-relations
description: 'Delete a single person relationship by its uuid, if permitted for the current subject.' description: 'Delete a single person relation by its uuid, if permitted for the current subject.'
operationId: deleteRelationshipByUuid operationId: deleteRelationByUuid
parameters: parameters:
- $ref: './auth.yaml#/components/parameters/currentUser' - $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles' - $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: relationshipUUID - name: relationUUID
in: path in: path
required: true required: true
schema: schema:
type: string type: string
format: uuid format: uuid
description: UUID of the relationship to delete. description: UUID of the relation to delete.
responses: responses:
"204": "204":
description: No Content description: No Content

View File

@ -1,9 +1,9 @@
get: get:
summary: Returns a list of (optionally filtered) person relationships for a given person. summary: Returns a list of (optionally filtered) person relations for a given person.
description: Returns the list of (optionally filtered) person relationships of a given person and which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) person relations of a given person and which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-office-relationships - hs-office-relations
operationId: listRelationships operationId: listRelations
parameters: parameters:
- $ref: './auth.yaml#/components/parameters/currentUser' - $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles' - $ref: './auth.yaml#/components/parameters/assumedRoles'
@ -13,13 +13,13 @@ get:
schema: schema:
type: string type: string
format: uuid format: uuid
description: Prefix of name properties from relHolder or contact to filter the results. description: Prefix of name properties from holder or contact to filter the results.
- name: relationshipType - name: relationType
in: query in: query
required: false required: false
schema: schema:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipType' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationType'
description: Prefix of name properties from relHolder or contact to filter the results. description: Prefix of name properties from holder or contact to filter the results.
responses: responses:
"200": "200":
description: OK description: OK
@ -28,17 +28,17 @@ get:
schema: schema:
type: array type: array
items: items:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401": "401":
$ref: './error-responses.yaml#/components/responses/Unauthorized' $ref: './error-responses.yaml#/components/responses/Unauthorized'
"403": "403":
$ref: './error-responses.yaml#/components/responses/Forbidden' $ref: './error-responses.yaml#/components/responses/Forbidden'
post: post:
summary: Adds a new person relationship. summary: Adds a new person relation.
tags: tags:
- hs-office-relationships - hs-office-relations
operationId: addRelationship operationId: addRelation
parameters: parameters:
- $ref: './auth.yaml#/components/parameters/currentUser' - $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles' - $ref: './auth.yaml#/components/parameters/assumedRoles'
@ -46,7 +46,7 @@ post:
content: content:
'application/json': 'application/json':
schema: schema:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipInsert' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
required: true required: true
responses: responses:
"201": "201":
@ -54,7 +54,7 @@ post:
content: content:
'application/json': 'application/json':
schema: schema:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship' $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401": "401":
$ref: './error-responses.yaml#/components/responses/Unauthorized' $ref: './error-responses.yaml#/components/responses/Unauthorized'
"403": "403":

View File

@ -35,13 +35,13 @@ paths:
$ref: "./hs-office-persons-with-uuid.yaml" $ref: "./hs-office-persons-with-uuid.yaml"
# Relationships # Relations
/api/hs/office/relationships: /api/hs/office/relations:
$ref: "./hs-office-relationships.yaml" $ref: "./hs-office-relations.yaml"
/api/hs/office/relationships/{relationshipUUID}: /api/hs/office/relations/{relationUUID}:
$ref: "./hs-office-relationships-with-uuid.yaml" $ref: "./hs-office-relations-with-uuid.yaml"
# BankAccounts # BankAccounts

View File

@ -0,0 +1,36 @@
--liquibase formatted sql
-- ============================================================================
--changeset hs-office-relation-MAIN-TABLE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
CREATE TYPE HsOfficeRelationType AS ENUM (
'UNKNOWN',
'PARTNER',
'EX_PARTNER',
'REPRESENTATIVE',
'DEBITOR',
'VIP_CONTACT',
'OPERATIONS',
'SUBSCRIBER');
CREATE CAST (character varying as HsOfficeRelationType) WITH INOUT AS IMPLICIT;
create table if not exists hs_office_relation
(
uuid uuid unique references RbacObject (uuid) initially deferred, -- on delete cascade
anchorUuid uuid not null references hs_office_person(uuid),
holderUuid uuid not null references hs_office_person(uuid),
contactUuid uuid references hs_office_contact(uuid),
type HsOfficeRelationType not null,
mark varchar(24)
);
--//
-- ============================================================================
--changeset hs-office-relation-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call create_journal('hs_office_relation');
--//

View File

@ -1,36 +0,0 @@
--liquibase formatted sql
-- ============================================================================
--changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
CREATE TYPE HsOfficeRelationshipType AS ENUM (
'UNKNOWN',
'PARTNER',
'EX_PARTNER',
'REPRESENTATIVE',
'DEBITOR',
'VIP_CONTACT',
'OPERATIONS',
'SUBSCRIBER');
CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT;
create table if not exists hs_office_relationship
(
uuid uuid unique references RbacObject (uuid) initially deferred, -- on delete cascade
relAnchorUuid uuid not null references hs_office_person(uuid),
relHolderUuid uuid not null references hs_office_person(uuid),
contactUuid uuid references hs_office_contact(uuid),
relType HsOfficeRelationshipType not null,
relMark varchar(24)
);
--//
-- ============================================================================
--changeset hs-office-relationship-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call create_journal('hs_office_relationship');
--//

View File

@ -1,4 +1,4 @@
### hs_office_relationship RBAC ### hs_office_relation RBAC
```mermaid ```mermaid
@ -28,17 +28,17 @@ subgraph hsOfficePerson
--> role:hsOfficePerson.guest[person.guest] --> role:hsOfficePerson.guest[person.guest]
end end
subgraph hsOfficeRelationship subgraph hsOfficeRelation
role:hsOfficePerson#relAnchor.admin[person#anchor.admin] role:hsOfficePerson#anchor.admin[person#anchor.admin]
--- role:hsOfficePerson.admin --- role:hsOfficePerson.admin
role:hsOfficeRelationship.owner[relationship.owner] role:hsOfficeRelation.owner[relation.owner]
%% permissions %% permissions
role:hsOfficeRelationship.owner --> perm:hsOfficeRelationship.*{{relationship.*}} role:hsOfficeRelation.owner --> perm:hsOfficeRelation.*{{relation.*}}
%% incoming %% incoming
role:global.admin ---> role:hsOfficeRelationship.owner role:global.admin ---> role:hsOfficeRelation.owner
role:hsOfficePersonAdmin#relAnchor.admin role:hsOfficePersonAdmin#anchor.admin
end end
``` ```

View File

@ -1,97 +1,97 @@
--liquibase formatted sql --liquibase formatted sql
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-rbac-OBJECT:1 endDelimiter:--// --changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRelatedRbacObject('hs_office_relationship'); call generateRelatedRbacObject('hs_office_relation');
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// --changeset hs-office-relation-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacRoleDescriptors('hsOfficeRelationship', 'hs_office_relationship'); call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-rbac-ROLES-CREATION:1 endDelimiter:--// --changeset hs-office-relation-rbac-ROLES-CREATION:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
Creates and updates the roles and their assignments for relationship entities. Creates and updates the roles and their assignments for relation entities.
*/ */
create or replace function hsOfficeRelationshipRbacRolesTrigger() create or replace function hsOfficeRelationRbacRolesTrigger()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
hsOfficeRelationshipTenant RbacRoleDescriptor; hsOfficeRelationTenant RbacRoleDescriptor;
newRelAnchor hs_office_person; newAnchor hs_office_person;
newRelHolder hs_office_person; newHolder hs_office_person;
oldContact hs_office_contact; oldContact hs_office_contact;
newContact hs_office_contact; newContact hs_office_contact;
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
hsOfficeRelationshipTenant := hsOfficeRelationshipTenant(NEW); hsOfficeRelationTenant := hsOfficeRelationTenant(NEW);
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newRelAnchor; select * from hs_office_person as p where p.uuid = NEW.anchorUuid into newAnchor;
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newRelHolder; select * from hs_office_person as p where p.uuid = NEW.holderUuid into newHolder;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact; select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationshipOwner(NEW), hsOfficeRelationOwner(NEW),
permissions => array['DELETE'], permissions => array['DELETE'],
incomingSuperRoles => array[ incomingSuperRoles => array[
globalAdmin(), globalAdmin(),
hsOfficePersonAdmin(newRelAnchor)] hsOfficePersonAdmin(newAnchor)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationshipAdmin(NEW), hsOfficeRelationAdmin(NEW),
permissions => array['UPDATE'], permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)] incomingSuperRoles => array[hsOfficeRelationOwner(NEW)]
); );
-- the tenant role for those related users who can view the data -- the tenant role for those related users who can view the data
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationshipTenant, hsOfficeRelationTenant,
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeRelationshipAdmin(NEW), hsOfficeRelationAdmin(NEW),
hsOfficePersonAdmin(newRelAnchor), hsOfficePersonAdmin(newAnchor),
hsOfficePersonAdmin(newRelHolder), hsOfficePersonAdmin(newHolder),
hsOfficeContactAdmin(newContact)], hsOfficeContactAdmin(newContact)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficePersonTenant(newRelAnchor), hsOfficePersonTenant(newAnchor),
hsOfficePersonTenant(newRelHolder), hsOfficePersonTenant(newHolder),
hsOfficeContactTenant(newContact)] hsOfficeContactTenant(newContact)]
); );
-- anchor and holder admin roles need each others tenant role -- anchor and holder admin roles need each others tenant role
-- to be able to see the joined relationship -- to be able to see the joined relation
-- TODO: this can probably be avoided through agent+guest roles -- TODO: this can probably be avoided through agent+guest roles
call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder)); call grantRoleToRole(hsOfficePersonTenant(newAnchor), hsOfficePersonAdmin(newHolder));
call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor)); call grantRoleToRole(hsOfficePersonTenant(newHolder), hsOfficePersonAdmin(newAnchor));
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact)); call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newHolder), hsOfficeContactAdmin(newContact));
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
if OLD.contactUuid <> NEW.contactUuid then if OLD.contactUuid <> NEW.contactUuid then
-- nothing but the contact can be updated, -- nothing but the contact can be updated,
-- in other cases, a new relationship needs to be created and the old updated -- in other cases, a new relation needs to be created and the old updated
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact; select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) ); call revokeRoleFromRole( hsOfficeRelationTenant, hsOfficeContactAdmin(oldContact) );
call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) ); call grantRoleToRole( hsOfficeRelationTenant, hsOfficeContactAdmin(newContact) );
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant ); call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationTenant );
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant ); call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationTenant );
end if; end if;
else else
raise exception 'invalid usage of TRIGGER'; raise exception 'invalid usage of TRIGGER';
@ -104,39 +104,39 @@ end; $$;
/* /*
An AFTER INSERT TRIGGER which creates the role structure for a new customer. An AFTER INSERT TRIGGER which creates the role structure for a new customer.
*/ */
create trigger createRbacRolesForHsOfficeRelationship_Trigger create trigger createRbacRolesForHsOfficeRelation_Trigger
after insert after insert
on hs_office_relationship on hs_office_relation
for each row for each row
execute procedure hsOfficeRelationshipRbacRolesTrigger(); execute procedure hsOfficeRelationRbacRolesTrigger();
/* /*
An AFTER UPDATE TRIGGER which updates the role structure of a customer. An AFTER UPDATE TRIGGER which updates the role structure of a customer.
*/ */
create trigger updateRbacRolesForHsOfficeRelationship_Trigger create trigger updateRbacRolesForHsOfficeRelation_Trigger
after update after update
on hs_office_relationship on hs_office_relation
for each row for each row
execute procedure hsOfficeRelationshipRbacRolesTrigger(); execute procedure hsOfficeRelationRbacRolesTrigger();
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$ call generateRbacIdentityViewFromProjection('hs_office_relation', $idName$
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid) (select idName from hs_office_person_iv p where p.uuid = target.anchorUuid)
|| '-with-' || target.relType || '-' || || '-with-' || target.type || '-' ||
(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid) (select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
$idName$); $idName$);
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--// --changeset hs-office-relation-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_relationship', call generateRbacRestrictedView('hs_office_relation',
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)', '(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)',
$updates$ $updates$
contactUuid = new.contactUuid contactUuid = new.contactUuid
$updates$); $updates$);
@ -146,10 +146,10 @@ call generateRbacRestrictedView('hs_office_relationship',
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--// --changeset hs-office-relation-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
Creates a global permission for new-relationship and assigns it to the hostsharing admins role. Creates a global permission for new-relation and assigns it to the hostsharing admins role.
*/ */
do language plpgsql $$ do language plpgsql $$
declare declare
@ -157,11 +157,11 @@ do language plpgsql $$
globalObjectUuid uuid; globalObjectUuid uuid;
globalAdminRoleUuid uuid ; globalAdminRoleUuid uuid ;
begin begin
call defineContext('granting global new-relationship permission to global admin role', null, null, null); call defineContext('granting global new-relation permission to global admin role', null, null, null);
globalAdminRoleUuid := findRoleId(globalAdmin()); globalAdminRoleUuid := findRoleId(globalAdmin());
globalObjectUuid := (select uuid from global); globalObjectUuid := (select uuid from global);
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relationship']); addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relation']);
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions); call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
end; end;
$$; $$;
@ -169,24 +169,24 @@ $$;
/** /**
Used by the trigger to prevent the add-customer to current user respectively assumed roles. Used by the trigger to prevent the add-customer to current user respectively assumed roles.
*/ */
create or replace function addHsOfficeRelationshipNotAllowedForCurrentSubjects() create or replace function addHsOfficeRelationNotAllowedForCurrentSubjects()
returns trigger returns trigger
language PLPGSQL language PLPGSQL
as $$ as $$
begin begin
raise exception '[403] new-relationship not permitted for %', raise exception '[403] new-relation not permitted for %',
array_to_string(currentSubjects(), ';', 'null'); array_to_string(currentSubjects(), ';', 'null');
end; $$; end; $$;
/** /**
Checks if the user or assumed roles are allowed to create a new customer. Checks if the user or assumed roles are allowed to create a new customer.
*/ */
create trigger hs_office_relationship_insert_trigger create trigger hs_office_relation_insert_trigger
before insert before insert
on hs_office_relationship on hs_office_relation
for each row for each row
-- TODO.spec: who is allowed to create new relationships -- TODO.spec: who is allowed to create new relations
when ( not hasAssumedRole() ) when ( not hasAssumedRole() )
execute procedure addHsOfficeRelationshipNotAllowedForCurrentSubjects(); execute procedure addHsOfficeRelationNotAllowedForCurrentSubjects();
--// --//

View File

@ -2,15 +2,15 @@
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-TEST-DATA-GENERATOR:1 endDelimiter:--// --changeset hs-office-relation-TEST-DATA-GENERATOR:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
Creates a single relationship test record. Creates a single relation test record.
*/ */
create or replace procedure createHsOfficeRelationshipTestData( create or replace procedure createHsOfficeRelationTestData(
holderPersonName varchar, holderPersonName varchar,
relationshipType HsOfficeRelationshipType, relationType HsOfficeRelationType,
anchorPersonTradeName varchar, anchorPersonTradeName varchar,
contactLabel varchar, contactLabel varchar,
mark varchar default null) mark varchar default null)
@ -24,7 +24,7 @@ declare
begin begin
idName := cleanIdentifier( anchorPersonTradeName || '-' || holderPersonName); idName := cleanIdentifier( anchorPersonTradeName || '-' || holderPersonName);
currentTask := 'creating relationship test-data ' || idName; currentTask := 'creating relation test-data ' || idName;
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin'); call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
execute format('set local hsadminng.currentTask to %L', currentTask); execute format('set local hsadminng.currentTask to %L', currentTask);
@ -45,20 +45,20 @@ begin
raise exception 'contact "%" not found', contactLabel; raise exception 'contact "%" not found', contactLabel;
end if; end if;
raise notice 'creating test relationship: %', idName; raise notice 'creating test relation: %', idName;
raise notice '- using anchor person (%): %', anchorPerson.uuid, anchorPerson; raise notice '- using anchor person (%): %', anchorPerson.uuid, anchorPerson;
raise notice '- using holder person (%): %', holderPerson.uuid, holderPerson; raise notice '- using holder person (%): %', holderPerson.uuid, holderPerson;
raise notice '- using contact (%): %', contact.uuid, contact; raise notice '- using contact (%): %', contact.uuid, contact;
insert insert
into hs_office_relationship (uuid, relanchoruuid, relholderuuid, reltype, relmark, contactUuid) into hs_office_relation (uuid, anchoruuid, holderuuid, type, mark, contactUuid)
values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationshipType, mark, contact.uuid); values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationType, mark, contact.uuid);
end; $$; end; $$;
--// --//
/* /*
Creates a range of test relationship for mass data generation. Creates a range of test relation for mass data generation.
*/ */
create or replace procedure createHsOfficeRelationshipTestData( create or replace procedure createHsOfficeRelationTestData(
startCount integer, -- count of auto generated rows before the run startCount integer, -- count of auto generated rows before the run
endCount integer -- count of auto generated rows after the run endCount integer -- count of auto generated rows after the run
) )
@ -72,7 +72,7 @@ begin
select p.* from hs_office_person p where tradeName = intToVarChar(t, 4) into person; select p.* from hs_office_person p where tradeName = intToVarChar(t, 4) into person;
select c.* from hs_office_contact c where c.label = intToVarChar(t, 4) || '#' || t into contact; select c.* from hs_office_contact c where c.label = intToVarChar(t, 4) || '#' || t into contact;
call createHsOfficeRelationshipTestData(person.uuid, contact.uuid, 'REPRESENTATIVE'); call createHsOfficeRelationTestData(person.uuid, contact.uuid, 'REPRESENTATIVE');
commit; commit;
end loop; end loop;
end; $$; end; $$;
@ -80,25 +80,25 @@ end; $$;
-- ============================================================================ -- ============================================================================
--changeset hs-office-relationship-TEST-DATA-GENERATION:1 context=dev,tc endDelimiter:--// --changeset hs-office-relation-TEST-DATA-GENERATION:1 context=dev,tc endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
do language plpgsql $$ do language plpgsql $$
begin begin
call createHsOfficeRelationshipTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact'); call createHsOfficeRelationTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact');
call createHsOfficeRelationshipTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact'); call createHsOfficeRelationTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
call createHsOfficeRelationshipTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact'); call createHsOfficeRelationTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
call createHsOfficeRelationshipTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact'); call createHsOfficeRelationTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
call createHsOfficeRelationshipTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact'); call createHsOfficeRelationTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
call createHsOfficeRelationshipTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact'); call createHsOfficeRelationTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
call createHsOfficeRelationshipTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact'); call createHsOfficeRelationTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact');
call createHsOfficeRelationshipTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact'); call createHsOfficeRelationTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact'); call createHsOfficeRelationTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce'); call createHsOfficeRelationTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
end; end;
$$; $$;
--// --//

View File

@ -33,9 +33,9 @@ create table hs_office_partner
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
partnerNumber numeric(5) unique not null, partnerNumber numeric(5) unique not null,
partnerRoleUuid uuid not null references hs_office_relationship(uuid), -- TODO: delete in after delete trigger partnerRelUuid uuid not null references hs_office_relation(uuid), -- TODO: delete in after delete trigger
personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRoleUuid personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRelUuid
contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRoleUuid contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRelUuid
detailsUuid uuid not null references hs_office_partner_details(uuid) -- deleted in after delete trigger detailsUuid uuid not null references hs_office_partner_details(uuid) -- deleted in after delete trigger
); );
--// --//

View File

@ -27,8 +27,8 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
oldPartnerRole hs_office_relationship; oldPartnerRel hs_office_relation;
newPartnerRole hs_office_relationship; newPartnerRel hs_office_relation;
oldPerson hs_office_person; oldPerson hs_office_person;
newPerson hs_office_person; newPerson hs_office_person;
@ -38,7 +38,7 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
select * from hs_office_relationship as r where r.uuid = NEW.partnerroleuuid into newPartnerRole; select * from hs_office_relation as r where r.uuid = NEW.partnerReluuid into newPartnerRel;
select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson; select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact; select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
@ -58,7 +58,7 @@ begin
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)], hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole), hsOfficeRelationTenant(newPartnerRel),
hsOfficePersonTenant(newPerson), hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)] hsOfficeContactTenant(newContact)]
); );
@ -67,7 +67,7 @@ begin
hsOfficePartnerAgent(NEW), hsOfficePartnerAgent(NEW),
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerAdmin(NEW), hsOfficePartnerAdmin(NEW),
hsOfficeRelationshipAdmin(newPartnerRole), hsOfficeRelationAdmin(newPartnerRel),
hsOfficePersonAdmin(newPerson), hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact)] hsOfficeContactAdmin(newContact)]
); );
@ -77,7 +77,7 @@ begin
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerAgent(NEW)], hsOfficePartnerAgent(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole), hsOfficeRelationTenant(newPartnerRel),
hsOfficePersonGuest(newPerson), hsOfficePersonGuest(newPerson),
hsOfficeContactGuest(newContact)] hsOfficeContactGuest(newContact)]
); );
@ -118,17 +118,17 @@ begin
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then if OLD.partnerRelUuid <> NEW.partnerRelUuid then
select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRole; select * from hs_office_relation as r where r.uuid = OLD.partnerRelUuid into oldPartnerRel;
call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPartnerRole), hsOfficePartnerAdmin(OLD)); call revokeRoleFromRole(hsOfficeRelationTenant(oldPartnerRel), hsOfficePartnerAdmin(OLD));
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRole), hsOfficePartnerAdmin(NEW)); call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficePartnerAdmin(NEW));
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationshipAdmin(oldPartnerRole)); call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationAdmin(oldPartnerRel));
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationshipAdmin(newPartnerRole)); call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationAdmin(newPartnerRel));
call revokeRoleFromRole(hsOfficeRelationshipGuest(oldPartnerRole), hsOfficePartnerTenant(OLD)); call revokeRoleFromRole(hsOfficeRelationGuest(oldPartnerRel), hsOfficePartnerTenant(OLD));
call grantRoleToRole(hsOfficeRelationshipGuest(newPartnerRole), hsOfficePartnerTenant(NEW)); call grantRoleToRole(hsOfficeRelationGuest(newPartnerRel), hsOfficePartnerTenant(NEW));
end if; end if;
if OLD.personUuid <> NEW.personUuid then if OLD.personUuid <> NEW.personUuid then
@ -202,7 +202,7 @@ call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
call generateRbacRestrictedView('hs_office_partner', call generateRbacRestrictedView('hs_office_partner',
'(select idName from hs_office_person_iv p where p.uuid = target.personUuid)', '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)',
$updates$ $updates$
partnerRoleUuid = new.partnerRoleUuid, partnerRelUuid = new.partnerRelUuid,
personUuid = new.personUuid, personUuid = new.personUuid,
contactUuid = new.contactUuid contactUuid = new.contactUuid
$updates$); $updates$);

View File

@ -18,7 +18,7 @@ declare
currentTask varchar; currentTask varchar;
idName varchar; idName varchar;
mandantPerson hs_office_person; mandantPerson hs_office_person;
partnerRole hs_office_relationship; partnerRel hs_office_relation;
relatedPerson hs_office_person; relatedPerson hs_office_person;
relatedContact hs_office_contact; relatedContact hs_office_contact;
relatedDetailsUuid uuid; relatedDetailsUuid uuid;
@ -42,16 +42,16 @@ begin
where c.label = contactLabel where c.label = contactLabel
into relatedContact; into relatedContact;
select r.* from hs_office_relationship r select r.* from hs_office_relation r
where r.reltype = 'PARTNER' where r.type = 'PARTNER'
and r.relanchoruuid = mandantPerson.uuid and r.relholderuuid = relatedPerson.uuid and r.anchoruuid = mandantPerson.uuid and r.holderuuid = relatedPerson.uuid
into partnerRole; into partnerRel;
if partnerRole is null then if partnerRel is null then
raise exception 'partnerRole "%"-"%" not found', mandantPerson.tradename, partnerPersonName; raise exception 'partnerRel "%"-"%" not found', mandantPerson.tradename, partnerPersonName;
end if; end if;
raise notice 'creating test partner: %', idName; raise notice 'creating test partner: %', idName;
raise notice '- using partnerRole (%): %', partnerRole.uuid, partnerRole; raise notice '- using partnerRel (%): %', partnerRel.uuid, partnerRel;
raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson; raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson;
raise notice '- using contact (%): %', relatedContact.uuid, relatedContact; raise notice '- using contact (%): %', relatedContact.uuid, relatedContact;
@ -68,8 +68,8 @@ begin
end if; end if;
insert insert
into hs_office_partner (uuid, partnerNumber, partnerRoleUuid, personuuid, contactuuid, detailsUuid) into hs_office_partner (uuid, partnerNumber, partnerRelUuid, personuuid, contactuuid, detailsUuid)
values (uuid_generate_v4(), partnerNumber, partnerRole.uuid, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); values (uuid_generate_v4(), partnerNumber, partnerRel.uuid, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
end; $$; end; $$;
--// --//

View File

@ -68,11 +68,11 @@ databaseChangeLog:
- include: - include:
file: db/changelog/218-hs-office-person-test-data.sql file: db/changelog/218-hs-office-person-test-data.sql
- include: - include:
file: db/changelog/220-hs-office-relationship.sql file: db/changelog/220-hs-office-relation.sql
- include: - include:
file: db/changelog/223-hs-office-relationship-rbac.sql file: db/changelog/223-hs-office-relation-rbac.sql
- include: - include:
file: db/changelog/228-hs-office-relationship-test-data.sql file: db/changelog/228-hs-office-relation-test-data.sql
- include: - include:
file: db/changelog/230-hs-office-partner.sql file: db/changelog/230-hs-office-partner.sql
- include: - include:

View File

@ -41,7 +41,7 @@ public class ArchitectureTest {
"..hs.office.migration", "..hs.office.migration",
"..hs.office.partner", "..hs.office.partner",
"..hs.office.person", "..hs.office.person",
"..hs.office.relationship", "..hs.office.relation",
"..hs.office.sepamandate", "..hs.office.sepamandate",
"..errors", "..errors",
"..mapper", "..mapper",
@ -148,7 +148,7 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeContactPackageRule = classes() public static final ArchRule hsOfficeContactPackageRule = classes()
.that().resideInAPackage("..hs.office.contact..") .that().resideInAPackage("..hs.office.contact..")
.should().onlyBeAccessed().byClassesThat() .should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.contact..", "..hs.office.relationship..", .resideInAnyPackage("..hs.office.contact..", "..hs.office.relation..",
"..hs.office.partner..", "..hs.office.partner..",
"..hs.office.debitor..", "..hs.office.debitor..",
"..hs.office.membership..", "..hs.office.membership..",
@ -159,7 +159,7 @@ public class ArchitectureTest {
public static final ArchRule hsOfficePersonPackageRule = classes() public static final ArchRule hsOfficePersonPackageRule = classes()
.that().resideInAPackage("..hs.office.person..") .that().resideInAPackage("..hs.office.person..")
.should().onlyBeAccessed().byClassesThat() .should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.person..", "..hs.office.relationship..", .resideInAnyPackage("..hs.office.person..", "..hs.office.relation..",
"..hs.office.partner..", "..hs.office.partner..",
"..hs.office.debitor..", "..hs.office.debitor..",
"..hs.office.membership..", "..hs.office.membership..",
@ -167,10 +167,10 @@ public class ArchitectureTest {
@ArchTest @ArchTest
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static final ArchRule hsOfficeRelationshipPackageRule = classes() public static final ArchRule hsOfficeRelationPackageRule = classes()
.that().resideInAPackage("..hs.office.relationship..") .that().resideInAPackage("..hs.office.relation..")
.should().onlyBeAccessed().byClassesThat() .should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.relationship..", .resideInAnyPackage("..hs.office.relation..",
"..hs.office.partner..", "..hs.office.partner..",
"..hs.office.migration.."); "..hs.office.migration..");

View File

@ -18,8 +18,8 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
@ -127,7 +127,7 @@ public class ImportOfficeData extends ContextBasedTest {
new String[]{"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"}, new String[]{"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"},
SUBSCRIBER_ROLES); SUBSCRIBER_ROLES);
static int relationshipId = 2000000; static int relationId = 2000000;
@Value("${spring.datasource.url}") @Value("${spring.datasource.url}")
private String jdbcUrl; private String jdbcUrl;
@ -144,7 +144,7 @@ public class ImportOfficeData extends ContextBasedTest {
private static Map<Integer, HsOfficeDebitorEntity> debitors = new WriteOnceMap<>(); private static Map<Integer, HsOfficeDebitorEntity> debitors = new WriteOnceMap<>();
private static Map<Integer, HsOfficeMembershipEntity> memberships = new WriteOnceMap<>(); private static Map<Integer, HsOfficeMembershipEntity> memberships = new WriteOnceMap<>();
private static Map<Integer, HsOfficeRelationshipEntity> relationships = new WriteOnceMap<>(); private static Map<Integer, HsOfficeRelationEntity> relations = new WriteOnceMap<>();
private static Map<Integer, HsOfficeSepaMandateEntity> sepaMandates = new WriteOnceMap<>(); private static Map<Integer, HsOfficeSepaMandateEntity> sepaMandates = new WriteOnceMap<>();
private static Map<Integer, HsOfficeBankAccountEntity> bankAccounts = new WriteOnceMap<>(); private static Map<Integer, HsOfficeBankAccountEntity> bankAccounts = new WriteOnceMap<>();
private static Map<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new WriteOnceMap<>(); private static Map<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new WriteOnceMap<>();
@ -220,17 +220,17 @@ public class ImportOfficeData extends ContextBasedTest {
@Test @Test
@Order(1021) @Order(1021)
void buildDebitorRelationships() { void buildDebitorRelations() {
debitors.forEach( (id, debitor) -> { debitors.forEach( (id, debitor) -> {
final var debitorRel = HsOfficeRelationshipEntity.builder() final var debitorRel = HsOfficeRelationEntity.builder()
.relType(HsOfficeRelationshipType.DEBITOR) .type(HsOfficeRelationType.DEBITOR)
.relAnchor(debitor.getPartner().getPartnerRole().getRelHolder()) .anchor(debitor.getPartner().getPartnerRel().getHolder())
.relHolder(debitor.getPartner().getPartnerRole().getRelHolder()) // just 1 debitor/partner in legacy hsadmin .holder(debitor.getPartner().getPartnerRel().getHolder()) // just 1 debitor/partner in legacy hsadmin
.contact(debitor.getBillingContact()) .contact(debitor.getBillingContact())
.build(); .build();
if (debitorRel.getRelAnchor() != null && debitorRel.getRelHolder() != null && if (debitorRel.getAnchor() != null && debitorRel.getHolder() != null &&
debitorRel.getContact() != null ) { debitorRel.getContact() != null ) {
relationships.put(relationshipId++, debitorRel); relations.put(relationId++, debitorRel);
} }
}); });
} }
@ -288,28 +288,28 @@ public class ImportOfficeData extends ContextBasedTest {
22=Membership(M-1102200, ?? Test PS, D-1102200, [2021-04-01,), NONE) 22=Membership(M-1102200, ?? Test PS, D-1102200, [2021-04-01,), NONE)
} }
"""); """);
assertThat(toFormattedString(relationships)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace("""
{ {
2000000=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000000=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000001=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000001=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000002=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000002=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000003=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='null null, null'), 2000003=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'),
2000004=rel(relAnchor='NP Mellies, Michael', relType='OPERATIONS', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000004=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000005=rel(relAnchor='NP Mellies, Michael', relType='REPRESENTATIVE', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000005=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000006=rel(relAnchor='LP JM GmbH', relType='EX_PARTNER', relHolder='LP JM e.K.', contact='JM e.K.'), 2000006=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
2000007=rel(relAnchor='LP JM GmbH', relType='OPERATIONS', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000007=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000008=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000008=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000009=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='operations-announce', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000009=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000010=rel(relAnchor='LP JM GmbH', relType='REPRESENTATIVE', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000010=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000011=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='members-announce', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000011=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000012=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='customers-announce', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000012=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000013=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'), 2000013=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
2000014=rel(relAnchor='?? Test PS', relType='OPERATIONS', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000014=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000015=rel(relAnchor='?? Test PS', relType='REPRESENTATIVE', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000015=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000016=rel(relAnchor='NP Mellies, Michael', relType='SUBSCRIBER', relMark='operations-announce', relHolder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '), 2000016=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
2000017=rel(relAnchor='NP Mellies, Michael', relType='DEBITOR', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000017=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000018=rel(relAnchor='LP JM GmbH', relType='DEBITOR', relHolder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'), 2000018=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
2000019=rel(relAnchor='?? Test PS', relType='DEBITOR', relHolder='?? Test PS', contact='Petra Schmidt , Test PS') 2000019=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS')
} }
"""); """);
} }
@ -419,20 +419,20 @@ public class ImportOfficeData extends ContextBasedTest {
@Test @Test
@Order(2009) @Order(2009)
void removeEmptyRelationships() { void removeEmptyRelations() {
assumeThatWeAreImportingControlledTestData(); assumeThatWeAreImportingControlledTestData();
// avoid a error when persisting the deliberetely invalid partner entry #99 // avoid a error when persisting the deliberetely invalid partner entry #99
final var idsToRemove = new HashSet<Integer>(); final var idsToRemove = new HashSet<Integer>();
relationships.forEach( (id, r) -> { relations.forEach( (id, r) -> {
// such a record // such a record
if (r.getContact() == null || r.getContact().getLabel() == null || if (r.getContact() == null || r.getContact().getLabel() == null ||
r.getRelHolder() == null | r.getRelHolder().getPersonType() == null ) { r.getHolder() == null | r.getHolder().getPersonType() == null ) {
idsToRemove.add(id); idsToRemove.add(id);
} }
}); });
assertThat(idsToRemove.size()).isEqualTo(1); // only from partner #99 (partner+contractual roles) assertThat(idsToRemove.size()).isEqualTo(1); // only from partner #99 (partner+contractual roles)
idsToRemove.forEach(id -> relationships.remove(id)); idsToRemove.forEach(id -> relations.remove(id));
} }
@Test @Test
@ -495,7 +495,7 @@ public class ImportOfficeData extends ContextBasedTest {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context(rbacSuperuser); context(rbacSuperuser);
relationships.forEach(this::persist); relations.forEach(this::persist);
}).assertSuccessful(); }).assertSuccessful();
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
@ -572,7 +572,7 @@ public class ImportOfficeData extends ContextBasedTest {
em.createNativeQuery("delete from hs_office_bankaccount where true").executeUpdate(); em.createNativeQuery("delete from hs_office_bankaccount where true").executeUpdate();
em.createNativeQuery("delete from hs_office_partner where true").executeUpdate(); em.createNativeQuery("delete from hs_office_partner where true").executeUpdate();
em.createNativeQuery("delete from hs_office_partner_details where true").executeUpdate(); em.createNativeQuery("delete from hs_office_partner_details where true").executeUpdate();
em.createNativeQuery("delete from hs_office_relationship where true").executeUpdate(); em.createNativeQuery("delete from hs_office_relation where true").executeUpdate();
em.createNativeQuery("delete from hs_office_contact where true").executeUpdate(); em.createNativeQuery("delete from hs_office_contact where true").executeUpdate();
em.createNativeQuery("delete from hs_office_person where true").executeUpdate(); em.createNativeQuery("delete from hs_office_person where true").executeUpdate();
}).assertSuccessful(); }).assertSuccessful();
@ -676,18 +676,18 @@ public class ImportOfficeData extends ContextBasedTest {
.forEach(rec -> { .forEach(rec -> {
final var person = HsOfficePersonEntity.builder().build(); final var person = HsOfficePersonEntity.builder().build();
final var partnerRelationship = HsOfficeRelationshipEntity.builder() final var partnerRelation = HsOfficeRelationEntity.builder()
.relHolder(person) .holder(person)
.relType(HsOfficeRelationshipType.PARTNER) .type(HsOfficeRelationType.PARTNER)
.relAnchor(mandant) .anchor(mandant)
.contact(null) // is set during contacts import depending on assigned roles .contact(null) // is set during contacts import depending on assigned roles
.build(); .build();
relationships.put(relationshipId++, partnerRelationship); relations.put(relationId++, partnerRelation);
final var partner = HsOfficePartnerEntity.builder() final var partner = HsOfficePartnerEntity.builder()
.partnerNumber(rec.getInteger("member_id")) .partnerNumber(rec.getInteger("member_id"))
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
.partnerRole(partnerRelationship) .partnerRel(partnerRelation)
.contact(null) // is set during contacts import depending on assigned roles .contact(null) // is set during contacts import depending on assigned roles
.person(person) .person(person)
.build(); .build();
@ -845,7 +845,7 @@ public class ImportOfficeData extends ContextBasedTest {
final var debitor = debitors.get(bpId); final var debitor = debitors.get(bpId);
final var partnerPerson = partner.getPerson(); final var partnerPerson = partner.getPerson();
if (containsPartnerRole(rec)) { if (containsPartnerRel(rec)) {
initPerson(partner.getPerson(), rec); initPerson(partner.getPerson(), rec);
} }
@ -859,46 +859,46 @@ public class ImportOfficeData extends ContextBasedTest {
final var contact = HsOfficeContactEntity.builder().build(); final var contact = HsOfficeContactEntity.builder().build();
initContact(contact, rec); initContact(contact, rec);
if (containsPartnerRole(rec)) { if (containsPartnerRel(rec)) {
assertThat(partner.getContact()).isNull(); assertThat(partner.getContact()).isNull();
partner.setContact(contact); partner.setContact(contact);
partner.getPartnerRole().setContact(contact); partner.getPartnerRel().setContact(contact);
} }
if (containsRole(rec, "billing")) { if (containsRole(rec, "billing")) {
assertThat(debitor.getBillingContact()).isNull(); assertThat(debitor.getBillingContact()).isNull();
debitor.setBillingContact(contact); debitor.setBillingContact(contact);
} }
if (containsRole(rec, "operation")) { if (containsRole(rec, "operation")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.OPERATIONS); addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.OPERATIONS);
} }
if (containsRole(rec, "contractual")) { if (containsRole(rec, "contractual")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.REPRESENTATIVE); addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.REPRESENTATIVE);
} }
if (containsRole(rec, "ex-partner")) { if (containsRole(rec, "ex-partner")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.EX_PARTNER); addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.EX_PARTNER);
} }
if (containsRole(rec, "vip-contact")) { if (containsRole(rec, "vip-contact")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT); addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.VIP_CONTACT);
} }
for (String subscriberRole: SUBSCRIBER_ROLES) { for (String subscriberRole: SUBSCRIBER_ROLES) {
if (containsRole(rec, subscriberRole)) { if (containsRole(rec, subscriberRole)) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.SUBSCRIBER) addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.SUBSCRIBER)
.setRelMark(subscriberRole.split(":")[1]) .setMark(subscriberRole.split(":")[1])
; ;
} }
} }
verifyContainsOnlyKnownRoles(rec.getString("roles")); verifyContainsOnlyKnownRoles(rec.getString("roles"));
}); });
optionallyAddMissingContractualRelationships(); optionallyAddMissingContractualRelations();
} }
private static void optionallyAddMissingContractualRelationships() { private static void optionallyAddMissingContractualRelations() {
final var contractualMissing = new HashSet<Integer>(); final var contractualMissing = new HashSet<Integer>();
partners.forEach( (id, partner) -> { partners.forEach( (id, partner) -> {
final var partnerPerson = partner.getPerson(); final var partnerPerson = partner.getPerson();
if (relationships.values().stream() if (relations.values().stream()
.filter(rel -> rel.getRelAnchor() == partnerPerson && rel.getRelType() == HsOfficeRelationshipType.REPRESENTATIVE) .filter(rel -> rel.getAnchor() == partnerPerson && rel.getType() == HsOfficeRelationType.REPRESENTATIVE)
.findFirst().isEmpty()) { .findFirst().isEmpty()) {
contractualMissing.add(partner.getPartnerNumber()); contractualMissing.add(partner.getPartnerNumber());
} }
@ -909,22 +909,22 @@ public class ImportOfficeData extends ContextBasedTest {
return ("," + roles + ",").contains("," + role + ","); return ("," + roles + ",").contains("," + role + ",");
} }
private static boolean containsPartnerRole(final Record rec) { private static boolean containsPartnerRel(final Record rec) {
return containsRole(rec, "partner"); return containsRole(rec, "partner");
} }
private static HsOfficeRelationshipEntity addRelationship( private static HsOfficeRelationEntity addRelation(
final HsOfficePersonEntity partnerPerson, final HsOfficePersonEntity partnerPerson,
final HsOfficePersonEntity contactPerson, final HsOfficePersonEntity contactPerson,
final HsOfficeContactEntity contact, final HsOfficeContactEntity contact,
final HsOfficeRelationshipType representative) { final HsOfficeRelationType representative) {
final var rel = HsOfficeRelationshipEntity.builder() final var rel = HsOfficeRelationEntity.builder()
.relAnchor(partnerPerson) .anchor(partnerPerson)
.relHolder(contactPerson) .holder(contactPerson)
.contact(contact) .contact(contact)
.relType(representative) .type(representative)
.build(); .build();
relationships.put(relationshipId++, rel); relations.put(relationId++, rel);
return rel; return rel;
} }

View File

@ -7,9 +7,9 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.test.Accepts; import net.hostsharing.test.Accepts;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
@ -41,7 +41,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRepository partnerRepo;
@Autowired @Autowired
HsOfficeRelationshipRepository relationshipRepository; HsOfficeRelationRepository relationRepository;
@Autowired @Autowired
HsOfficePersonRepository personRepo; HsOfficePersonRepository personRepo;
@ -102,9 +102,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body(""" .body("""
{ {
"partnerNumber": "20002", "partnerNumber": "20002",
"partnerRole": { "partnerRel": {
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
}, },
"personUuid": "%s", "personUuid": "%s",
@ -155,9 +155,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body(""" .body("""
{ {
"partnerNumber": "20003", "partnerNumber": "20003",
"partnerRole": { "partnerRel": {
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
}, },
"personUuid": "%s", "personUuid": "%s",
@ -193,9 +193,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body(""" .body("""
{ {
"partnerNumber": "20004", "partnerNumber": "20004",
"partnerRole": { "partnerRel": {
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
}, },
"personUuid": "%s", "personUuid": "%s",
@ -413,7 +413,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
// then the given partner is gone // then the given partner is gone
assertThat(partnerRepo.findByUuid(givenPartner.getUuid())).isEmpty(); assertThat(partnerRepo.findByUuid(givenPartner.getUuid())).isEmpty();
assertThat(relationshipRepository.findByUuid(givenPartner.getPartnerRole().getUuid())).isEmpty(); assertThat(relationRepository.findByUuid(givenPartner.getPartnerRel().getUuid())).isEmpty();
} }
@Test @Test
@ -465,15 +465,15 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
final var partnerRole = new HsOfficeRelationshipEntity(); final var partnerRel = new HsOfficeRelationEntity();
partnerRole.setRelType(HsOfficeRelationshipType.PARTNER); partnerRel.setType(HsOfficeRelationType.PARTNER);
partnerRole.setRelAnchor(givenMandantPerson); partnerRel.setAnchor(givenMandantPerson);
partnerRole.setRelHolder(givenPerson); partnerRel.setHolder(givenPerson);
partnerRole.setContact(givenContact); partnerRel.setContact(givenContact);
em.persist(partnerRole); em.persist(partnerRel);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.partnerRole(partnerRole) .partnerRel(partnerRel)
.partnerNumber(partnerNumber) .partnerNumber(partnerNumber)
.person(givenPerson) .person(givenPerson)
.contact(givenContact) .contact(givenContact)
@ -492,6 +492,6 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
cleanupAllNew(HsOfficePartnerEntity.class); cleanupAllNew(HsOfficePartnerEntity.class);
// TODO: should not be necessary anymore, once it's deleted via after delete trigger // TODO: should not be necessary anymore, once it's deleted via after delete trigger
cleanupAllNew(HsOfficeRelationshipEntity.class); cleanupAllNew(HsOfficeRelationEntity.class);
} }
} }

View File

@ -3,8 +3,8 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
@ -54,7 +54,7 @@ class HsOfficePartnerControllerRestTest {
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRepository partnerRepo;
@MockBean @MockBean
HsOfficeRelationshipRepository relationshipRepo; HsOfficeRelationRepository relationRepo;
@MockBean @MockBean
EntityManager em; EntityManager em;
@ -100,9 +100,9 @@ class HsOfficePartnerControllerRestTest {
.content(""" .content("""
{ {
"partnerNumber": "20002", "partnerNumber": "20002",
"partnerRole": { "partnerRel": {
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
}, },
"personUuid": "%s", "personUuid": "%s",
@ -137,9 +137,9 @@ class HsOfficePartnerControllerRestTest {
.content(""" .content("""
{ {
"partnerNumber": "20002", "partnerNumber": "20002",
"partnerRole": { "partnerRel": {
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
}, },
"personUuid": "%s", "personUuid": "%s",
@ -175,11 +175,11 @@ class HsOfficePartnerControllerRestTest {
when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock)); when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock));
when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(0); when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(0);
final UUID givenRelationshipUuid = UUID.randomUUID(); final UUID givenRelationUuid = UUID.randomUUID();
when(partnerMock.getPartnerRole()).thenReturn(HsOfficeRelationshipEntity.builder() when(partnerMock.getPartnerRel()).thenReturn(HsOfficeRelationEntity.builder()
.uuid(givenRelationshipUuid) .uuid(givenRelationUuid)
.build()); .build());
when(relationshipRepo.deleteByUuid(givenRelationshipUuid)).thenReturn(0); when(relationRepo.deleteByUuid(givenRelationUuid)).thenReturn(0);
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
@ -193,18 +193,18 @@ class HsOfficePartnerControllerRestTest {
} }
@Test @Test
void respondBadRequest_ifRelationshipCannotBeDeleted() throws Exception { void respondBadRequest_ifRelationCannotBeDeleted() throws Exception {
// given // given
final UUID givenPartnerUuid = UUID.randomUUID(); final UUID givenPartnerUuid = UUID.randomUUID();
when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock)); when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock));
when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(1); when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(1);
when(relationshipRepo.deleteByUuid(any())).thenReturn(0); when(relationRepo.deleteByUuid(any())).thenReturn(0);
final UUID givenRelationshipUuid = UUID.randomUUID(); final UUID givenRelationUuid = UUID.randomUUID();
when(partnerMock.getPartnerRole()).thenReturn(HsOfficeRelationshipEntity.builder() when(partnerMock.getPartnerRel()).thenReturn(HsOfficeRelationEntity.builder()
.uuid(givenRelationshipUuid) .uuid(givenRelationUuid)
.build()); .build());
when(relationshipRepo.deleteByUuid(givenRelationshipUuid)).thenReturn(0); when(relationRepo.deleteByUuid(givenRelationUuid)).thenReturn(0);
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders

View File

@ -3,9 +3,9 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository; import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
@ -43,7 +43,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRepository partnerRepo;
@Autowired @Autowired
HsOfficeRelationshipRepository relationshipRepo; HsOfficeRelationRepository relationRepo;
@Autowired @Autowired
HsOfficePersonRepository personRepo; HsOfficePersonRepository personRepo;
@ -80,19 +80,19 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0); final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder() final var partnerRel = HsOfficeRelationEntity.builder()
.relHolder(givenPartnerPerson) .holder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER) .type(HsOfficeRelationType.PARTNER)
.relAnchor(givenMandantorPerson) .anchor(givenMandantorPerson)
.contact(givenContact) .contact(givenContact)
.build(); .build();
relationshipRepo.save(partnerRole); relationRepo.save(partnerRel);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(20031) .partnerNumber(20031)
.partnerRole(partnerRole) .partnerRel(partnerRel)
.person(givenPartnerPerson) .person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder() .details(HsOfficePartnerDetailsEntity.builder()
@ -125,17 +125,17 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0); final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var newRelationship = HsOfficeRelationshipEntity.builder() final var newRelation = HsOfficeRelationEntity.builder()
.relHolder(givenPartnerPerson) .holder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER) .type(HsOfficeRelationType.PARTNER)
.relAnchor(givenMandantPerson) .anchor(givenMandantPerson)
.contact(givenContact) .contact(givenContact)
.build(); .build();
relationshipRepo.save(newRelationship); relationRepo.save(newRelation);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(20032) .partnerNumber(20032)
.partnerRole(newRelationship) .partnerRel(newRelation)
.person(givenPartnerPerson) .person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
@ -146,9 +146,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then // then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_relationship#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin", "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin",
"hs_office_relationship#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.owner", "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.owner",
"hs_office_relationship#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant", "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant",
"hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.admin", "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.admin",
"hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.agent", "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.agent",
"hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.owner", "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.owner",
@ -160,25 +160,25 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(distinct(fromFormatted( .containsExactlyInAnyOrder(distinct(fromFormatted(
initialGrantNames, initialGrantNames,
// relationship - TODO: check and cleanup // relation - TODO: check and cleanup
"{ grant role person#HostsharingeG.tenant to role person#EBess.admin by system and assume }", "{ grant role person#HostsharingeG.tenant to role person#EBess.admin by system and assume }",
"{ grant role person#EBess.tenant to role person#HostsharingeG.admin by system and assume }", "{ grant role person#EBess.tenant to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.tenant by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.tenant by system and assume }",
"{ grant role partner#20032:EBess-4th.agent to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }", "{ grant role partner#20032:EBess-4th.agent to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
"{ grant perm UPDATE on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }", "{ grant perm UPDATE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
"{ grant perm DELETE on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }", "{ grant perm DELETE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.admin to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }", "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
"{ grant perm SELECT on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }", "{ grant perm SELECT on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role contact#4th.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }", "{ grant role contact#4th.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role person#EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }", "{ grant role person#EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role person#HostsharingeG.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }", "{ grant role person#HostsharingeG.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
// owner // owner
"{ grant perm DELETE on partner#20032:EBess-4th to role partner#20032:EBess-4th.owner by system and assume }", "{ grant perm DELETE on partner#20032:EBess-4th to role partner#20032:EBess-4th.owner by system and assume }",
@ -426,15 +426,15 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
// TODO: should deleting a partner automatically delete the PARTNER relationship? (same for debitor) // TODO: should deleting a partner automatically delete the PARTNER relation? (same for debitor)
// TODO: why did the test cleanup check does not notice this, if missing? // TODO: why did the test cleanup check does not notice this, if missing?
return partnerRepo.deleteByUuid(givenPartner.getUuid()) + return partnerRepo.deleteByUuid(givenPartner.getUuid()) +
relationshipRepo.deleteByUuid(givenPartner.getPartnerRole().getUuid()); relationRepo.deleteByUuid(givenPartner.getPartnerRel().getUuid());
}); });
// then // then
result.assertSuccessful(); result.assertSuccessful();
assertThat(result.returnedValue()).isEqualTo(2); // partner+relationship assertThat(result.returnedValue()).isEqualTo(2); // partner+relation
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(initialRoleNames); assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(initialRoleNames);
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(initialGrantNames); assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(initialGrantNames);
} }
@ -466,17 +466,17 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0); final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder() final var partnerRel = HsOfficeRelationEntity.builder()
.relHolder(givenPartnerPerson) .holder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER) .type(HsOfficeRelationType.PARTNER)
.relAnchor(givenMandantorPerson) .anchor(givenMandantorPerson)
.contact(givenContact) .contact(givenContact)
.build(); .build();
relationshipRepo.save(partnerRole); relationRepo.save(partnerRel);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber) .partnerNumber(partnerNumber)
.partnerRole(partnerRole) .partnerRel(partnerRel)
.person(givenPartnerPerson) .person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
@ -502,7 +502,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
void cleanup() { void cleanup() {
cleanupAllNew(HsOfficePartnerDetailsEntity.class); // TODO: should not be necessary cleanupAllNew(HsOfficePartnerDetailsEntity.class); // TODO: should not be necessary
cleanupAllNew(HsOfficePartnerEntity.class); cleanupAllNew(HsOfficePartnerEntity.class);
cleanupAllNew(HsOfficeRelationshipEntity.class); cleanupAllNew(HsOfficeRelationEntity.class);
} }
private String[] distinct(final String[] strings) { private String[] distinct(final String[] strings) {

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
@ -7,7 +7,7 @@ import net.hostsharing.test.Accepts;
import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationshipTypeResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
import org.json.JSONException; import org.json.JSONException;
@ -31,7 +31,7 @@ import static org.hamcrest.Matchers.startsWith;
classes = { HsadminNgApplication.class, JpaAttempt.class } classes = { HsadminNgApplication.class, JpaAttempt.class }
) )
@Transactional @Transactional
class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithCleanup { class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithCleanup {
public static final UUID GIVEN_NON_EXISTING_HOLDER_PERSON_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); public static final UUID GIVEN_NON_EXISTING_HOLDER_PERSON_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
@LocalServerPort @LocalServerPort
@ -44,7 +44,7 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
Context contextMock; Context contextMock;
@Autowired @Autowired
HsOfficeRelationshipRepository relationshipRepo; HsOfficeRelationRepository relationRepo;
@Autowired @Autowired
HsOfficePersonRepository personRepo; HsOfficePersonRepository personRepo;
@ -56,11 +56,11 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@Nested @Nested
@Accepts({ "Relationship:F(Find)" }) @Accepts({ "Relation:F(Find)" })
class ListRelationships { class ListRelations {
@Test @Test
void globalAdmin_withoutAssumedRoles_canViewAllRelationshipsOfGivenPersonAndType_ifNoCriteriaGiven() throws JSONException { void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType_ifNoCriteriaGiven() throws JSONException {
// given // given
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
@ -71,45 +71,45 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/relationships?personUuid=%s&relationshipType=%s" .get("http://localhost/api/hs/office/relations?personUuid=%s&relationType=%s"
.formatted(givenPerson.getUuid(), HsOfficeRelationshipTypeResource.PARTNER)) .formatted(givenPerson.getUuid(), HsOfficeRelationTypeResource.PARTNER))
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
[ [
{ {
"relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" }, "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
"relHolder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH" }, "holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH" },
"relType": "PARTNER", "type": "PARTNER",
"relMark": null, "mark": null,
"contact": { "label": "first contact" } "contact": { "label": "first contact" }
}, },
{ {
"relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" }, "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
"relHolder": { "personType": "INCORPORATED_FIRM", "tradeName": "Fourth eG" }, "holder": { "personType": "INCORPORATED_FIRM", "tradeName": "Fourth eG" },
"relType": "PARTNER", "type": "PARTNER",
"contact": { "label": "fourth contact" } "contact": { "label": "fourth contact" }
}, },
{ {
"relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" }, "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
"relHolder": { "personType": "LEGAL_PERSON", "tradeName": "Second e.K.", "givenName": "Peter", "familyName": "Smith" }, "holder": { "personType": "LEGAL_PERSON", "tradeName": "Second e.K.", "givenName": "Peter", "familyName": "Smith" },
"relType": "PARTNER", "type": "PARTNER",
"relMark": null, "mark": null,
"contact": { "label": "second contact" } "contact": { "label": "second contact" }
}, },
{ {
"relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" }, "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
"relHolder": { "personType": "NATURAL_PERSON", "givenName": "Peter", "familyName": "Smith" }, "holder": { "personType": "NATURAL_PERSON", "givenName": "Peter", "familyName": "Smith" },
"relType": "PARTNER", "type": "PARTNER",
"relMark": null, "mark": null,
"contact": { "label": "sixth contact" } "contact": { "label": "sixth contact" }
}, },
{ {
"relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" }, "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
"relHolder": { "personType": "INCORPORATED_FIRM", "tradeName": "Third OHG" }, "holder": { "personType": "INCORPORATED_FIRM", "tradeName": "Third OHG" },
"relType": "PARTNER", "type": "PARTNER",
"relMark": null, "mark": null,
"contact": { "label": "third contact" } "contact": { "label": "third contact" }
} }
] ]
@ -119,11 +119,11 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
} }
@Nested @Nested
@Accepts({ "Relationship:C(Create)" }) @Accepts({ "Relation:C(Create)" })
class AddRelationship { class AddRelation {
@Test @Test
void globalAdmin_withoutAssumedRole_canAddRelationship() { void globalAdmin_withoutAssumedRole_canAddRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
@ -136,38 +136,38 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
"relType": "%s", "type": "%s",
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
} }
""".formatted( """.formatted(
HsOfficeRelationshipTypeResource.ACCOUNTING, HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPerson.getUuid(), givenAnchorPerson.getUuid(),
givenHolderPerson.getUuid(), givenHolderPerson.getUuid(),
givenContact.getUuid())) givenContact.getUuid()))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/relationships") .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(201) .statusCode(201)
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body("uuid", isUuidValid()) .body("uuid", isUuidValid())
.body("relType", is("ACCOUNTING")) .body("type", is("DEBITOR"))
.body("relAnchor.tradeName", is("Third OHG")) .body("anchor.tradeName", is("Third OHG"))
.body("relHolder.givenName", is("Paul")) .body("holder.givenName", is("Paul"))
.body("contact.label", is("second contact")) .body("contact.label", is("second contact"))
.header("Location", startsWith("http://localhost")) .header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on .extract().header("Location"); // @formatter:on
// finally, the new relationship can be accessed under the generated UUID // finally, the new relation can be accessed under the generated UUID
final var newUserUuid = toCleanup(HsOfficeRelationshipEntity.class, UUID.fromString( final var newUserUuid = toCleanup(HsOfficeRelationEntity.class, UUID.fromString(
location.substring(location.lastIndexOf('/') + 1))); location.substring(location.lastIndexOf('/') + 1)));
assertThat(newUserUuid).isNotNull(); assertThat(newUserUuid).isNotNull();
} }
@Test @Test
void globalAdmin_canNotAddRelationship_ifAnchorPersonDoesNotExist() { void globalAdmin_canNotAddRelation_ifAnchorPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAnchorPersonUuid = GIVEN_NON_EXISTING_HOLDER_PERSON_UUID; final var givenAnchorPersonUuid = GIVEN_NON_EXISTING_HOLDER_PERSON_UUID;
@ -180,27 +180,27 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
"relType": "%s", "type": "%s",
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
} }
""".formatted( """.formatted(
HsOfficeRelationshipTypeResource.ACCOUNTING, HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPersonUuid, givenAnchorPersonUuid,
givenHolderPerson.getUuid(), givenHolderPerson.getUuid(),
givenContact.getUuid())) givenContact.getUuid()))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/relationships") .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(404) .statusCode(404)
.body("message", is("cannot find relAnchorUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID)); .body("message", is("cannot find anchorUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID));
// @formatter:on // @formatter:on
} }
@Test @Test
void globalAdmin_canNotAddRelationship_ifHolderPersonDoesNotExist() { void globalAdmin_canNotAddRelation_ifHolderPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
@ -212,27 +212,27 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
"relType": "%s", "type": "%s",
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
} }
""".formatted( """.formatted(
HsOfficeRelationshipTypeResource.ACCOUNTING, HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPerson.getUuid(), givenAnchorPerson.getUuid(),
GIVEN_NON_EXISTING_HOLDER_PERSON_UUID, GIVEN_NON_EXISTING_HOLDER_PERSON_UUID,
givenContact.getUuid())) givenContact.getUuid()))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/relationships") .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(404) .statusCode(404)
.body("message", is("cannot find relHolderUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID)); .body("message", is("cannot find holderUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID));
// @formatter:on // @formatter:on
} }
@Test @Test
void globalAdmin_canNotAddRelationship_ifContactDoesNotExist() { void globalAdmin_canNotAddRelation_ifContactDoesNotExist() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
@ -245,19 +245,19 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
"relType": "%s", "type": "%s",
"relAnchorUuid": "%s", "anchorUuid": "%s",
"relHolderUuid": "%s", "holderUuid": "%s",
"contactUuid": "%s" "contactUuid": "%s"
} }
""".formatted( """.formatted(
HsOfficeRelationshipTypeResource.ACCOUNTING, HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPerson.getUuid(), givenAnchorPerson.getUuid(),
givenHolderPerson.getUuid(), givenHolderPerson.getUuid(),
givenContactUuid)) givenContactUuid))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/relationships") .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(404) .statusCode(404)
.body("message", is("cannot find contactUuid 00000000-0000-0000-0000-000000000000")); .body("message", is("cannot find contactUuid 00000000-0000-0000-0000-000000000000"));
@ -266,97 +266,97 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
} }
@Nested @Nested
@Accepts({ "Relationship:R(Read)" }) @Accepts({ "Relation:R(Read)" })
class GetRelationship { class GetRelation {
@Test @Test
void globalAdmin_withoutAssumedRole_canGetArbitraryRelationship() { void globalAdmin_withoutAssumedRole_canGetArbitraryRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final UUID givenRelationshipUuid = findRelationship("First", "Firby").getUuid(); final UUID givenRelationUuid = findRelation("First", "Firby").getUuid();
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/relationships/" + givenRelationshipUuid) .get("http://localhost/api/hs/office/relations/" + givenRelationUuid)
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
{ {
"relAnchor": { "tradeName": "First GmbH" }, "anchor": { "tradeName": "First GmbH" },
"relHolder": { "familyName": "Firby" }, "holder": { "familyName": "Firby" },
"contact": { "label": "first contact" } "contact": { "label": "first contact" }
} }
""")); // @formatter:on """)); // @formatter:on
} }
@Test @Test
@Accepts({ "Relationship:X(Access Control)" }) @Accepts({ "Relation:X(Access Control)" })
void normalUser_canNotGetUnrelatedRelationship() { void normalUser_canNotGetUnrelatedRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final UUID givenRelationshipUuid = findRelationship("First", "Firby").getUuid(); final UUID givenRelationUuid = findRelation("First", "Firby").getUuid();
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "selfregistered-user-drew@hostsharing.org") .header("current-user", "selfregistered-user-drew@hostsharing.org")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/relationships/" + givenRelationshipUuid) .get("http://localhost/api/hs/office/relations/" + givenRelationUuid)
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(404); // @formatter:on .statusCode(404); // @formatter:on
} }
@Test @Test
@Accepts({ "Relationship:X(Access Control)" }) @Accepts({ "Relation:X(Access Control)" })
void contactAdminUser_canGetRelatedRelationship() { void contactAdminUser_canGetRelatedRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenRelationship = findRelationship("First", "Firby"); final var givenRelation = findRelation("First", "Firby");
assertThat(givenRelationship.getContact().getLabel()).isEqualTo("first contact"); assertThat(givenRelation.getContact().getLabel()).isEqualTo("first contact");
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "contact-admin@firstcontact.example.com") .header("current-user", "contact-admin@firstcontact.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid()) .get("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
{ {
"relAnchor": { "tradeName": "First GmbH" }, "anchor": { "tradeName": "First GmbH" },
"relHolder": { "familyName": "Firby" }, "holder": { "familyName": "Firby" },
"contact": { "label": "first contact" } "contact": { "label": "first contact" }
} }
""")); // @formatter:on """)); // @formatter:on
} }
} }
private HsOfficeRelationshipEntity findRelationship( private HsOfficeRelationEntity findRelation(
final String anchorPersonName, final String anchorPersonName,
final String holderPersoneName) { final String holderPersoneName) {
final var anchorPersonUuid = personRepo.findPersonByOptionalNameLike(anchorPersonName).get(0).getUuid(); final var anchorPersonUuid = personRepo.findPersonByOptionalNameLike(anchorPersonName).get(0).getUuid();
final var holderPersonUuid = personRepo.findPersonByOptionalNameLike(holderPersoneName).get(0).getUuid(); final var holderPersonUuid = personRepo.findPersonByOptionalNameLike(holderPersoneName).get(0).getUuid();
final var givenRelationship = relationshipRepo final var givenRelation = relationRepo
.findRelationshipRelatedToPersonUuid(anchorPersonUuid) .findRelationRelatedToPersonUuid(anchorPersonUuid)
.stream() .stream()
.filter(r -> r.getRelHolder().getUuid().equals(holderPersonUuid)) .filter(r -> r.getHolder().getUuid().equals(holderPersonUuid))
.findFirst().orElseThrow(); .findFirst().orElseThrow();
return givenRelationship; return givenRelation;
} }
@Nested @Nested
@Accepts({ "Relationship:U(Update)" }) @Accepts({ "Relation:U(Update)" })
class PatchRelationship { class PatchRelation {
@Test @Test
void globalAdmin_withoutAssumedRole_canPatchContactOfArbitraryRelationship() { void globalAdmin_withoutAssumedRole_canPatchContactOfArbitraryRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler(); final var givenRelation = givenSomeTemporaryRelationBessler();
assertThat(givenRelationship.getContact().getLabel()).isEqualTo("seventh contact"); assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
@ -370,109 +370,109 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
""".formatted(givenContact.getUuid())) """.formatted(givenContact.getUuid()))
.port(port) .port(port)
.when() .when()
.patch("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid()) .patch("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(200) .statusCode(200)
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body("uuid", isUuidValid()) .body("uuid", isUuidValid())
.body("relType", is("REPRESENTATIVE")) .body("type", is("REPRESENTATIVE"))
.body("relAnchor.tradeName", is("Erben Bessler")) .body("anchor.tradeName", is("Erben Bessler"))
.body("relHolder.familyName", is("Winkler")) .body("holder.familyName", is("Winkler"))
.body("contact.label", is("fourth contact")); .body("contact.label", is("fourth contact"));
// @formatter:on // @formatter:on
// finally, the relationship is actually updated // finally, the relation is actually updated
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isPresent().get() assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isPresent().get()
.matches(rel -> { .matches(rel -> {
assertThat(rel.getRelAnchor().getTradeName()).contains("Bessler"); assertThat(rel.getAnchor().getTradeName()).contains("Bessler");
assertThat(rel.getRelHolder().getFamilyName()).contains("Winkler"); assertThat(rel.getHolder().getFamilyName()).contains("Winkler");
assertThat(rel.getContact().getLabel()).isEqualTo("fourth contact"); assertThat(rel.getContact().getLabel()).isEqualTo("fourth contact");
assertThat(rel.getRelType()).isEqualTo(HsOfficeRelationshipType.REPRESENTATIVE); assertThat(rel.getType()).isEqualTo(HsOfficeRelationType.REPRESENTATIVE);
return true; return true;
}); });
} }
} }
@Nested @Nested
@Accepts({ "Relationship:D(Delete)" }) @Accepts({ "Relation:D(Delete)" })
class DeleteRelationship { class DeleteRelation {
@Test @Test
void globalAdmin_withoutAssumedRole_canDeleteArbitraryRelationship() { void globalAdmin_withoutAssumedRole_canDeleteArbitraryRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler(); final var givenRelation = givenSomeTemporaryRelationBessler();
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.delete("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid()) .delete("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(204); // @formatter:on .statusCode(204); // @formatter:on
// then the given relationship is gone // then the given relation is gone
assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isEmpty(); assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isEmpty();
} }
@Test @Test
@Accepts({ "Relationship:X(Access Control)" }) @Accepts({ "Relation:X(Access Control)" })
void contactAdminUser_canNotDeleteRelatedRelationship() { void contactAdminUser_canNotDeleteRelatedRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler(); final var givenRelation = givenSomeTemporaryRelationBessler();
assertThat(givenRelationship.getContact().getLabel()).isEqualTo("seventh contact"); assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "contact-admin@seventhcontact.example.com") .header("current-user", "contact-admin@seventhcontact.example.com")
.port(port) .port(port)
.when() .when()
.delete("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid()) .delete("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(403); // @formatter:on .statusCode(403); // @formatter:on
// then the given relationship is still there // then the given relation is still there
assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isNotEmpty(); assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isNotEmpty();
} }
@Test @Test
@Accepts({ "Relationship:X(Access Control)" }) @Accepts({ "Relation:X(Access Control)" })
void normalUser_canNotDeleteUnrelatedRelationship() { void normalUser_canNotDeleteUnrelatedRelation() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler(); final var givenRelation = givenSomeTemporaryRelationBessler();
assertThat(givenRelationship.getContact().getLabel()).isEqualTo("seventh contact"); assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "selfregistered-user-drew@hostsharing.org") .header("current-user", "selfregistered-user-drew@hostsharing.org")
.port(port) .port(port)
.when() .when()
.delete("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid()) .delete("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(404); // @formatter:on .statusCode(404); // @formatter:on
// then the given relationship is still there // then the given relation is still there
assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isNotEmpty(); assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isNotEmpty();
} }
} }
private HsOfficeRelationshipEntity givenSomeTemporaryRelationshipBessler() { private HsOfficeRelationEntity givenSomeTemporaryRelationBessler() {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Winkler").get(0); final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Winkler").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("seventh contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("seventh contact").get(0);
final var newRelationship = HsOfficeRelationshipEntity.builder() final var newRelation = HsOfficeRelationEntity.builder()
.relType(HsOfficeRelationshipType.REPRESENTATIVE) .type(HsOfficeRelationType.REPRESENTATIVE)
.relAnchor(givenAnchorPerson) .anchor(givenAnchorPerson)
.relHolder(givenHolderPerson) .holder(givenHolderPerson)
.contact(givenContact) .contact(givenContact)
.build(); .build();
assertThat(toCleanup(relationshipRepo.save(newRelationship))).isEqualTo(newRelationship); assertThat(toCleanup(relationRepo.save(newRelation))).isEqualTo(newRelation);
return newRelationship; return newRelation;
}).assertSuccessful().returnedValue(); }).assertSuccessful().returnedValue();
} }

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationshipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationPatchResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.test.PatchUnitTestBase; import net.hostsharing.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -21,12 +21,12 @@ import static org.mockito.Mockito.lenient;
@TestInstance(PER_CLASS) @TestInstance(PER_CLASS)
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase< class HsOfficeRelationEntityPatcherUnitTest extends PatchUnitTestBase<
HsOfficeRelationshipPatchResource, HsOfficeRelationPatchResource,
HsOfficeRelationshipEntity HsOfficeRelationEntity
> { > {
static final UUID INITIAL_RELATIONSHIP_UUID = UUID.randomUUID(); static final UUID INITIAL_RELATION_UUID = UUID.randomUUID();
static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID(); static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
@Mock @Mock
@ -49,24 +49,24 @@ class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase<
.build(); .build();
@Override @Override
protected HsOfficeRelationshipEntity newInitialEntity() { protected HsOfficeRelationEntity newInitialEntity() {
final var entity = new HsOfficeRelationshipEntity(); final var entity = new HsOfficeRelationEntity();
entity.setUuid(INITIAL_RELATIONSHIP_UUID); entity.setUuid(INITIAL_RELATION_UUID);
entity.setRelType(HsOfficeRelationshipType.REPRESENTATIVE); entity.setType(HsOfficeRelationType.REPRESENTATIVE);
entity.setRelAnchor(givenInitialAnchorPerson); entity.setAnchor(givenInitialAnchorPerson);
entity.setRelHolder(givenInitialHolderPerson); entity.setHolder(givenInitialHolderPerson);
entity.setContact(givenInitialContact); entity.setContact(givenInitialContact);
return entity; return entity;
} }
@Override @Override
protected HsOfficeRelationshipPatchResource newPatchResource() { protected HsOfficeRelationPatchResource newPatchResource() {
return new HsOfficeRelationshipPatchResource(); return new HsOfficeRelationPatchResource();
} }
@Override @Override
protected HsOfficeRelationshipEntityPatcher createPatcher(final HsOfficeRelationshipEntity relationship) { protected HsOfficeRelationEntityPatcher createPatcher(final HsOfficeRelationEntity relation) {
return new HsOfficeRelationshipEntityPatcher(em, relationship); return new HsOfficeRelationEntityPatcher(em, relation);
} }
@Override @Override
@ -74,9 +74,9 @@ class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase<
return Stream.of( return Stream.of(
new JsonNullableProperty<>( new JsonNullableProperty<>(
"contact", "contact",
HsOfficeRelationshipPatchResource::setContactUuid, HsOfficeRelationPatchResource::setContactUuid,
PATCHED_CONTACT_UUID, PATCHED_CONTACT_UUID,
HsOfficeRelationshipEntity::setContact, HsOfficeRelationEntity::setContact,
newContact(PATCHED_CONTACT_UUID)) newContact(PATCHED_CONTACT_UUID))
.notNullable() .notNullable()
); );

View File

@ -0,0 +1,43 @@
package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class HsOfficeRelationEntityUnitTest {
private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.LEGAL_PERSON)
.tradeName("some trade name")
.build();
private HsOfficePersonEntity holder = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.NATURAL_PERSON)
.familyName("Meier")
.givenName("Mellie")
.build();
@Test
void toStringReturnsAllProperties() {
final var given = HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.SUBSCRIBER)
.mark("members-announce")
.anchor(anchor)
.holder(holder)
.build();
assertThat(given.toString()).isEqualTo("rel(anchor='LP some trade name', type='SUBSCRIBER', mark='members-announce', holder='NP Meier, Mellie')");
}
@Test
void toShortString() {
final var given = HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.REPRESENTATIVE)
.anchor(anchor)
.holder(holder)
.build();
assertThat(given.toShortString()).isEqualTo("rel(anchor='LP some trade name', type='REPRESENTATIVE', holder='NP Meier, Mellie')");
}
}

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.relationship; package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
@ -29,10 +29,10 @@ import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest @DataJpaTest
@Import( { Context.class, JpaAttempt.class }) @Import( { Context.class, JpaAttempt.class })
class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWithCleanup { class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired @Autowired
HsOfficeRelationshipRepository relationshipRepo; HsOfficeRelationRepository relationRepo;
@Autowired @Autowired
HsOfficePersonRepository personRepo; HsOfficePersonRepository personRepo;
@ -56,33 +56,33 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested
class CreateRelationship { class CreateRelation {
@Test @Test
public void testHostsharingAdmin_withoutAssumedRole_canCreateNewRelationship() { public void testHostsharingAdmin_withoutAssumedRole_canCreateNewRelation() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = relationshipRepo.count(); final var count = relationRepo.count();
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0); final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newRelationship = HsOfficeRelationshipEntity.builder() final var newRelation = HsOfficeRelationEntity.builder()
.relAnchor(givenAnchorPerson) .anchor(givenAnchorPerson)
.relHolder(givenHolderPerson) .holder(givenHolderPerson)
.relType(HsOfficeRelationshipType.REPRESENTATIVE) .type(HsOfficeRelationType.REPRESENTATIVE)
.contact(givenContact) .contact(givenContact)
.build(); .build();
return toCleanup(relationshipRepo.save(newRelationship)); return toCleanup(relationRepo.save(newRelation));
}); });
// then // then
result.assertSuccessful(); result.assertSuccessful();
assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeRelationshipEntity::getUuid).isNotNull(); assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeRelationEntity::getUuid).isNotNull();
assertThatRelationshipIsPersisted(result.returnedValue()); assertThatRelationIsPersisted(result.returnedValue());
assertThat(relationshipRepo.count()).isEqualTo(count + 1); assertThat(relationRepo.count()).isEqualTo(count + 1);
} }
@Test @Test
@ -97,190 +97,190 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0); final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
final var newRelationship = HsOfficeRelationshipEntity.builder() final var newRelation = HsOfficeRelationEntity.builder()
.relAnchor(givenAnchorPerson) .anchor(givenAnchorPerson)
.relHolder(givenHolderPerson) .holder(givenHolderPerson)
.relType(HsOfficeRelationshipType.REPRESENTATIVE) .type(HsOfficeRelationType.REPRESENTATIVE)
.contact(givenContact) .contact(givenContact)
.build(); .build();
return toCleanup(relationshipRepo.save(newRelationship)); return toCleanup(relationRepo.save(newRelation));
}); });
// then // then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin", "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin",
"hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner", "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner",
"hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant")); "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted( assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
"{ grant perm DELETE on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }", "{ grant perm DELETE on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }", "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }", "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }",
"{ grant perm UPDATE on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }", "{ grant perm UPDATE on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }", "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
"{ grant perm SELECT on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }", "{ grant perm SELECT on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#fourthcontact.admin by system and assume }", "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }", "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }", "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
"{ grant role hs_office_contact#fourthcontact.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }", "{ grant role hs_office_contact#fourthcontact.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
"{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }", "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
null) null)
); );
} }
private void assertThatRelationshipIsPersisted(final HsOfficeRelationshipEntity saved) { private void assertThatRelationIsPersisted(final HsOfficeRelationEntity saved) {
final var found = relationshipRepo.findByUuid(saved.getUuid()); final var found = relationRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved); assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
} }
} }
@Nested @Nested
class FindAllRelationships { class FindAllRelations {
@Test @Test
public void globalAdmin_withoutAssumedRole_canViewAllRelationshipsOfArbitraryPerson() { public void globalAdmin_withoutAssumedRole_canViewAllRelationsOfArbitraryPerson() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var person = personRepo.findPersonByOptionalNameLike("Second e.K.").stream().findFirst().orElseThrow(); final var person = personRepo.findPersonByOptionalNameLike("Second e.K.").stream().findFirst().orElseThrow();
// when // when
final var result = relationshipRepo.findRelationshipRelatedToPersonUuid(person.getUuid()); final var result = relationRepo.findRelationRelatedToPersonUuid(person.getUuid());
// then // then
allTheseRelationshipsAreReturned( allTheseRelationsAreReturned(
result, result,
"rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='LP Second e.K.', contact='second contact')", "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP Second e.K.', contact='second contact')",
"rel(relAnchor='LP Second e.K.', relType='REPRESENTATIVE', relHolder='NP Smith, Peter', contact='second contact')"); "rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')");
} }
@Test @Test
public void normalUser_canViewRelationshipsOfOwnedPersons() { public void normalUser_canViewRelationsOfOwnedPersons() {
// given: // given:
context("person-FirstGmbH@example.com"); context("person-FirstGmbH@example.com");
final var person = personRepo.findPersonByOptionalNameLike("First").stream().findFirst().orElseThrow(); final var person = personRepo.findPersonByOptionalNameLike("First").stream().findFirst().orElseThrow();
// when: // when:
final var result = relationshipRepo.findRelationshipRelatedToPersonUuid(person.getUuid()); final var result = relationRepo.findRelationRelatedToPersonUuid(person.getUuid());
// then: // then:
exactlyTheseRelationshipsAreReturned( exactlyTheseRelationsAreReturned(
result, result,
"rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='LP First GmbH', contact='first contact')", "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP First GmbH', contact='first contact')",
"rel(relAnchor='LP First GmbH', relType='REPRESENTATIVE', relHolder='NP Firby, Susan', contact='first contact')"); "rel(anchor='LP First GmbH', type='REPRESENTATIVE', holder='NP Firby, Susan', contact='first contact')");
} }
} }
@Nested @Nested
class UpdateRelationship { class UpdateRelation {
@Test @Test
public void hostsharingAdmin_withoutAssumedRole_canUpdateContactOfArbitraryRelationship() { public void hostsharingAdmin_withoutAssumedRole_canUpdateContactOfArbitraryRelation() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "fifth contact"); "Anita", "fifth contact");
assertThatRelationshipIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
givenRelationship, givenRelation,
"hs_office_person#ErbenBesslerMelBessler.admin"); "hs_office_person#ErbenBesslerMelBessler.admin");
assertThatRelationshipActuallyInDatabase(givenRelationship); assertThatRelationActuallyInDatabase(givenRelation);
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
givenRelationship.setContact(givenContact); givenRelation.setContact(givenContact);
return toCleanup(relationshipRepo.save(givenRelationship)); return toCleanup(relationRepo.save(givenRelation));
}); });
// then // then
result.assertSuccessful(); result.assertSuccessful();
assertThat(result.returnedValue().getContact().getLabel()).isEqualTo("sixth contact"); assertThat(result.returnedValue().getContact().getLabel()).isEqualTo("sixth contact");
assertThatRelationshipIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(), result.returnedValue(),
"global#global.admin"); "global#global.admin");
assertThatRelationshipIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(), result.returnedValue(),
"hs_office_contact#sixthcontact.admin"); "hs_office_contact#sixthcontact.admin");
assertThatRelationshipIsNotVisibleForUserWithRole( assertThatRelationIsNotVisibleForUserWithRole(
result.returnedValue(), result.returnedValue(),
"hs_office_contact#fifthcontact.admin"); "hs_office_contact#fifthcontact.admin");
relationshipRepo.deleteByUuid(givenRelationship.getUuid()); relationRepo.deleteByUuid(givenRelation.getUuid());
} }
@Test @Test
public void relHolderAdmin_canNotUpdateRelatedRelationship() { public void holderAdmin_canNotUpdateRelatedRelation() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "eighth"); "Anita", "eighth");
assertThatRelationshipIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
givenRelationship, givenRelation,
"hs_office_person#BesslerAnita.admin"); "hs_office_person#BesslerAnita.admin");
assertThatRelationshipActuallyInDatabase(givenRelationship); assertThatRelationActuallyInDatabase(givenRelation);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_person#BesslerAnita.admin"); context("superuser-alex@hostsharing.net", "hs_office_person#BesslerAnita.admin");
givenRelationship.setContact(null); givenRelation.setContact(null);
return relationshipRepo.save(givenRelationship); return relationRepo.save(givenRelation);
}); });
// then // then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class, result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
"[403] Subject ", " is not allowed to update hs_office_relationship uuid"); "[403] Subject ", " is not allowed to update hs_office_relation uuid");
} }
@Test @Test
public void contactAdmin_canNotUpdateRelatedRelationship() { public void contactAdmin_canNotUpdateRelatedRelation() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenRelationship = givenSomeTemporaryRelationshipBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "ninth"); "Anita", "ninth");
assertThatRelationshipIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
givenRelationship, givenRelation,
"hs_office_contact#ninthcontact.admin"); "hs_office_contact#ninthcontact.admin");
assertThatRelationshipActuallyInDatabase(givenRelationship); assertThatRelationActuallyInDatabase(givenRelation);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact.admin"); context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact.admin");
givenRelationship.setContact(null); // TODO givenRelation.setContact(null); // TODO
return relationshipRepo.save(givenRelationship); return relationRepo.save(givenRelation);
}); });
// then // then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class, result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
"[403] Subject ", " is not allowed to update hs_office_relationship uuid"); "[403] Subject ", " is not allowed to update hs_office_relation uuid");
} }
private void assertThatRelationshipActuallyInDatabase(final HsOfficeRelationshipEntity saved) { private void assertThatRelationActuallyInDatabase(final HsOfficeRelationEntity saved) {
final var found = relationshipRepo.findByUuid(saved.getUuid()); final var found = relationRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved); assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved);
} }
private void assertThatRelationshipIsVisibleForUserWithRole( private void assertThatRelationIsVisibleForUserWithRole(
final HsOfficeRelationshipEntity entity, final HsOfficeRelationEntity entity,
final String assumedRoles) { final String assumedRoles) {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles); context("superuser-alex@hostsharing.net", assumedRoles);
assertThatRelationshipActuallyInDatabase(entity); assertThatRelationActuallyInDatabase(entity);
}).assertSuccessful(); }).assertSuccessful();
} }
private void assertThatRelationshipIsNotVisibleForUserWithRole( private void assertThatRelationIsNotVisibleForUserWithRole(
final HsOfficeRelationshipEntity entity, final HsOfficeRelationEntity entity,
final String assumedRoles) { final String assumedRoles) {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles); context("superuser-alex@hostsharing.net", assumedRoles);
final var found = relationshipRepo.findByUuid(entity.getUuid()); final var found = relationRepo.findByUuid(entity.getUuid());
assertThat(found).isEmpty(); assertThat(found).isEmpty();
}).assertSuccessful(); }).assertSuccessful();
} }
@ -290,63 +290,63 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
class DeleteByUuid { class DeleteByUuid {
@Test @Test
public void globalAdmin_withoutAssumedRole_canDeleteAnyRelationship() { public void globalAdmin_withoutAssumedRole_canDeleteAnyRelation() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenRelationship = givenSomeTemporaryRelationshipBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "tenth"); "Anita", "tenth");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
relationshipRepo.deleteByUuid(givenRelationship.getUuid()); relationRepo.deleteByUuid(givenRelation.getUuid());
}); });
// then // then
result.assertSuccessful(); result.assertSuccessful();
assertThat(jpaAttempt.transacted(() -> { assertThat(jpaAttempt.transacted(() -> {
context("superuser-fran@hostsharing.net", null); context("superuser-fran@hostsharing.net", null);
return relationshipRepo.findByUuid(givenRelationship.getUuid()); return relationRepo.findByUuid(givenRelation.getUuid());
}).assertSuccessful().returnedValue()).isEmpty(); }).assertSuccessful().returnedValue()).isEmpty();
} }
@Test @Test
public void contactUser_canViewButNotDeleteTheirRelatedRelationship() { public void contactUser_canViewButNotDeleteTheirRelatedRelation() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenRelationship = givenSomeTemporaryRelationshipBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "eleventh"); "Anita", "eleventh");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("contact-admin@eleventhcontact.example.com"); context("contact-admin@eleventhcontact.example.com");
assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isPresent(); assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isPresent();
relationshipRepo.deleteByUuid(givenRelationship.getUuid()); relationRepo.deleteByUuid(givenRelation.getUuid());
}); });
// then // then
result.assertExceptionWithRootCauseMessage( result.assertExceptionWithRootCauseMessage(
JpaSystemException.class, JpaSystemException.class,
"[403] Subject ", " not allowed to delete hs_office_relationship"); "[403] Subject ", " not allowed to delete hs_office_relation");
assertThat(jpaAttempt.transacted(() -> { assertThat(jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
return relationshipRepo.findByUuid(givenRelationship.getUuid()); return relationRepo.findByUuid(givenRelation.getUuid());
}).assertSuccessful().returnedValue()).isPresent(); // still there }).assertSuccessful().returnedValue()).isPresent(); // still there
} }
@Test @Test
public void deletingARelationshipAlsoDeletesRelatedRolesAndGrants() { public void deletingARelationAlsoDeletesRelatedRolesAndGrants() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll())); final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll())); final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll()));
final var givenRelationship = givenSomeTemporaryRelationshipBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "twelfth"); "Anita", "twelfth");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
return relationshipRepo.deleteByUuid(givenRelationship.getUuid()); return relationRepo.deleteByUuid(givenRelation.getUuid());
}); });
// then // then
@ -363,7 +363,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select currentTask, targetTable, targetOp select currentTask, targetTable, targetOp
from tx_journal_v from tx_journal_v
where targettable = 'hs_office_relationship'; where targettable = 'hs_office_relation';
"""); """);
// when // when
@ -371,40 +371,40 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
// then // then
assertThat(customerLogEntries).map(Arrays::toString).contains( assertThat(customerLogEntries).map(Arrays::toString).contains(
"[creating relationship test-data HostsharingeG-FirstGmbH, hs_office_relationship, INSERT]", "[creating relation test-data HostsharingeG-FirstGmbH, hs_office_relation, INSERT]",
"[creating relationship test-data FirstGmbH-Firby, hs_office_relationship, INSERT]"); "[creating relation test-data FirstGmbH-Firby, hs_office_relation, INSERT]");
} }
private HsOfficeRelationshipEntity givenSomeTemporaryRelationshipBessler(final String holderPerson, final String contact) { private HsOfficeRelationEntity givenSomeTemporaryRelationBessler(final String holderPerson, final String contact) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike(holderPerson).get(0); final var givenHolderPerson = personRepo.findPersonByOptionalNameLike(holderPerson).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var newRelationship = HsOfficeRelationshipEntity.builder() final var newRelation = HsOfficeRelationEntity.builder()
.relType(HsOfficeRelationshipType.REPRESENTATIVE) .type(HsOfficeRelationType.REPRESENTATIVE)
.relAnchor(givenAnchorPerson) .anchor(givenAnchorPerson)
.relHolder(givenHolderPerson) .holder(givenHolderPerson)
.contact(givenContact) .contact(givenContact)
.build(); .build();
return toCleanup(relationshipRepo.save(newRelationship)); return toCleanup(relationRepo.save(newRelation));
}).assertSuccessful().returnedValue(); }).assertSuccessful().returnedValue();
} }
void exactlyTheseRelationshipsAreReturned( void exactlyTheseRelationsAreReturned(
final List<HsOfficeRelationshipEntity> actualResult, final List<HsOfficeRelationEntity> actualResult,
final String... relationshipNames) { final String... relationNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(HsOfficeRelationshipEntity::toString) .extracting(HsOfficeRelationEntity::toString)
.containsExactlyInAnyOrder(relationshipNames); .containsExactlyInAnyOrder(relationNames);
} }
void allTheseRelationshipsAreReturned( void allTheseRelationsAreReturned(
final List<HsOfficeRelationshipEntity> actualResult, final List<HsOfficeRelationEntity> actualResult,
final String... relationshipNames) { final String... relationNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(HsOfficeRelationshipEntity::toString) .extracting(HsOfficeRelationEntity::toString)
.contains(relationshipNames); .contains(relationNames);
} }
} }

View File

@ -1,44 +0,0 @@
package net.hostsharing.hsadminng.hs.office.relationship;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
class HsOfficeRelationshipEntityUnitTest {
private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.LEGAL_PERSON)
.tradeName("some trade name")
.build();
private HsOfficePersonEntity holder = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.NATURAL_PERSON)
.familyName("Meier")
.givenName("Mellie")
.build();
@Test
void toStringReturnsAllProperties() {
final var given = HsOfficeRelationshipEntity.builder()
.relType(HsOfficeRelationshipType.SUBSCRIBER)
.relMark("members-announce")
.relAnchor(anchor)
.relHolder(holder)
.build();
assertThat(given.toString()).isEqualTo("rel(relAnchor='LP some trade name', relType='SUBSCRIBER', relMark='members-announce', relHolder='NP Meier, Mellie')");
}
@Test
void toShortString() {
final var given = HsOfficeRelationshipEntity.builder()
.relType(HsOfficeRelationshipType.REPRESENTATIVE)
.relAnchor(anchor)
.relHolder(holder)
.build();
assertThat(given.toShortString()).isEqualTo("rel(relAnchor='LP some trade name', relType='REPRESENTATIVE', relHolder='NP Meier, Mellie')");
}
}

View File

@ -1,41 +0,0 @@
#!/bin/bash
sourceLower=partner
targetLower=relationship
sourceStudly=Partner
targetStudly=Relationship
## for source in `find src -iname ""*$sourceLower*"" -type f \( -iname \*.yaml -o -iname \*.sql -o -iname \*.java \)`; do
for source in `find src -iname ""*$sourceLower*"" -type f \( -iname \*.yaml \)`; do
target=`echo $source | sed -e "s/$sourceStudly/$targetStudly/g" -e "s/$sourceLower/$targetLower/g"`
echo "Generating $target from $source:"
mkdir -p `dirname $target`
sed -e 's/hs-office-partner/hs-office-relationship/g' \
-e 's/hs_office_partner/hs_office_relationship/g' \
-e 's/HsOfficePartner/HsOfficeRelationship/g' \
-e 's/hsOfficePartner/hsOfficeRelationship/g' \
-e 's/partner/relationship/g' \
\
-e 's/addPartner/addRelationship/g' \
-e 's/listPartners/listRelationships/g' \
-e 's/getPartnerByUuid/getRelationshipByUuid/g' \
-e 's/patchPartner/patchRelationship/g' \
-e 's/person/relHolder/g' \
-e 's/registrationOffice/relType/g' \
<$source >$target
done
exit
cat >>src/main/resources/db/changelog/db.changelog-master.yaml <<EOF
- include:
file: db/changelog/2X0-hs-office-$sourceLower.sql
- include:
file: db/changelog/2X3-hs-office-$sourceLower-rbac.sql
- include:
file: db/changelog/2X8-hs-office-$sourceLower-test-data.sql
EOF