improve-performance-of-office-data-import (#83)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #83
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig 2024-08-05 11:48:33 +02:00
parent e4e1216a85
commit 085876c772
106 changed files with 1244 additions and 941 deletions

View File

@ -0,0 +1,36 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ImportHostingAssets" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="env">
<map>
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
</map>
</option>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":importHostingAssets" />
<option value="--tests" />
<option value="&quot;net.hostsharing.hsadminng.hs.migration.ImportHostingAssets&quot;" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<EXTENSION ID="com.intellij.execution.ExternalSystemRunConfigurationJavaExtension">
<extension name="coverage" sample_coverage="false" />
</EXTENSION>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,36 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ImportOfficeData" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="env">
<map>
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
</map>
</option>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":importOfficeData" />
<option value="--tests" />
<option value="&quot;net.hostsharing.hsadminng.hs.migration.ImportOfficeData&quot;" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<EXTENSION ID="com.intellij.execution.ExternalSystemRunConfigurationJavaExtension">
<extension name="coverage" sample_coverage="false" />
</EXTENSION>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
</component>

1
.run/README.txt Normal file
View File

@ -0,0 +1 @@
Stored run-Configurations for IntelliJ IDEA.

View File

@ -121,8 +121,8 @@ WITH statements AS (
SELECT * FROM pg_stat_statements pss
)
SELECT calls,
total_exec_time::int/(60*1000) as total_exec_time_mins,
mean_exec_time::int as mean_exec_time_millis,
total_exec_time::int/(60*1000) as total_mins,
mean_exec_time::int as mean_millis,
query
FROM statements
WHERE calls > 100 AND shared_blks_hit > 0

View File

@ -46,6 +46,6 @@ public class CustomErrorResponse {
this.path = path;
this.statusCode = status.value();
this.statusPhrase = status.getReasonPhrase();
this.message = message;
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
}
}

View File

@ -0,0 +1,24 @@
package net.hostsharing.hsadminng.errors;
import jakarta.validation.constraints.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisplayAs {
class DisplayName {
public static String of(final Class<?> clazz) {
final var displayNameAnnot = clazz.getAnnotation(DisplayAs.class);
return displayNameAnnot != null ? displayNameAnnot.value() : clazz.getSimpleName();
}
public static String of(@NotNull final Object instance) {
return of(instance.getClass());
}
}
String value() default "";
}

View File

@ -1,12 +0,0 @@
package net.hostsharing.hsadminng.errors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisplayName {
String value() default "";
}

View File

@ -152,8 +152,8 @@ public class RestResponseEntityExceptionHandler
final var entityName = matcher.group(1);
final var entityClass = resolveClass(entityName);
if (entityClass.isPresent()) {
return (entityClass.get().isAnnotationPresent(DisplayName.class)
? exceptionMessage.replace(entityName, entityClass.get().getAnnotation(DisplayName.class).value())
return (entityClass.get().isAnnotationPresent(DisplayAs.class)
? exceptionMessage.replace(entityName, entityClass.get().getAnnotation(DisplayAs.class).value())
: exceptionMessage.replace(entityName, entityClass.get().getSimpleName()))
.replace(" with id ", " with uuid ");
}

View File

@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -23,7 +23,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("BookingDebitor")
@DisplayAs("BookingDebitor")
public class HsBookingDebitorEntity implements Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";

View File

@ -15,7 +15,7 @@ import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type;
@ -71,7 +71,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class HsBookingItemEntity implements Stringifyable, RbacObject<HsBookingItemEntity>, PropertiesProvider {
public class HsBookingItemEntity implements Stringifyable, BaseEntity<HsBookingItemEntity>, PropertiesProvider {
private static Stringify<HsBookingItemEntity> stringify = stringify(HsBookingItemEntity.class)
.withProp(HsBookingItemEntity::getProject)

View File

@ -3,10 +3,10 @@ package net.hostsharing.hsadminng.hs.booking.project;
import lombok.*;
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -34,7 +34,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class HsBookingProjectEntity implements Stringifyable, RbacObject<HsBookingProjectEntity> {
public class HsBookingProjectEntity implements Stringifyable, BaseEntity<HsBookingProjectEntity> {
private static Stringify<HsBookingProjectEntity> stringify = stringify(HsBookingProjectEntity.class)
.withProp(HsBookingProjectEntity::getDebitor)
@ -81,7 +81,7 @@ public class HsBookingProjectEntity implements Stringifyable, RbacObject<HsBooki
directlyFetchedByDependsOnColumn(),
NOT_NULL)
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class, usingCase(DEBITOR),
.importEntityAlias("debitorRel", HsOfficeRelationRbacEntity.class, usingCase(DEBITOR),
dependsOnColumn("debitorUuid"),
fetchedBySql("""
SELECT ${columns}

View File

@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.hosting.asset;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -16,7 +16,7 @@ import java.util.UUID;
import static java.util.Collections.emptyMap;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public interface HsHostingAsset extends Stringifyable, RbacObject<HsHostingAsset>, PropertiesProvider {
public interface HsHostingAsset extends Stringifyable, BaseEntity<HsHostingAsset>, PropertiesProvider {
Stringify<HsHostingAsset> stringify = stringify(HsHostingAsset.class)
.withProp(HsHostingAsset::getType)
@ -36,7 +36,7 @@ public interface HsHostingAsset extends Stringifyable, RbacObject<HsHostingAsset
String getIdentifier();
HsBookingItemEntity getBookingItem();
HsHostingAsset getAssignedToAsset();
HsOfficeContactEntity getAlarmContact();
HsOfficeContactRealEntity getAlarmContact();
List<? extends HsHostingAsset> getSubHostingAssets();
String getCaption();
Map<String, Object> getConfig();

View File

@ -8,7 +8,8 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@ -54,7 +55,6 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Builder
@Entity
@ -90,7 +90,7 @@ public class HsHostingAssetEntity implements HsHostingAsset {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "alarmcontactuuid")
private HsOfficeContactEntity alarmContact;
private HsOfficeContactRealEntity alarmContact;
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
@ -160,7 +160,7 @@ public class HsHostingAssetEntity implements HsHostingAsset {
directlyFetchedByDependsOnColumn(),
NULLABLE)
.importEntityAlias("alarmContact", HsOfficeContactEntity.class, usingDefaultCase(),
.importEntityAlias("alarmContact", HsOfficeContactRbacEntity.class, usingDefaultCase(),
dependsOnColumn("alarmContactUuid"),
directlyFetchedByDependsOnColumn(),
NULLABLE)

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset;
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetPatchResource;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.KeyValueMap;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
@ -29,7 +29,7 @@ public class HsHostingAssetEntityPatcher implements EntityPatcher<HsHostingAsset
// HOWTO: patch nullable JSON resource uuid to an ntity reference
.ifPresent(newValue -> entity.setAlarmContact(
Optional.ofNullable(newValue)
.map(uuid -> em.getReference(HsOfficeContactEntity.class, newValue))
.map(uuid -> em.getReference(HsOfficeContactRealEntity.class, newValue))
.orElse(null)));
}
}

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
@ -213,7 +213,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
}
}
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
static class AlarmContact extends ReferenceValidator<HsOfficeContactRealEntity, Enum<?>> {
AlarmContact(final HsHostingAssetType.RelationPolicy policy) {
super(policy, HsHostingAsset::getAlarmContact);

View File

@ -2,8 +2,8 @@ package net.hostsharing.hsadminng.hs.office.bankaccount;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -26,8 +26,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@DisplayName("BankAccount")
public class HsOfficeBankAccountEntity implements RbacObject<HsOfficeBankAccountEntity>, Stringifyable {
@DisplayAs("BankAccount")
public class HsOfficeBankAccountEntity implements BaseEntity<HsOfficeBankAccountEntity>, Stringifyable {
private static Stringify<HsOfficeBankAccountEntity> toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount")
.withIdProp(HsOfficeBankAccountEntity::getIban)

View File

@ -0,0 +1,106 @@
package net.hostsharing.hsadminng.hs.office.contact;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;
import jakarta.persistence.Version;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@MappedSuperclass
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@SuperBuilder(toBuilder = true)
@FieldNameConstants
@DisplayAs("Contact")
public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContact> {
private static Stringify<HsOfficeContact> toString = stringify(HsOfficeContact.class, "contact")
.withProp(Fields.caption, HsOfficeContact::getCaption)
.withProp(Fields.emailAddresses, HsOfficeContact::getEmailAddresses);
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid;
@Version
private int version;
@Column(name = "caption")
private String caption;
@Column(name = "postaladdress")
private String postalAddress; // multiline free-format text
@Builder.Default
@Setter(AccessLevel.NONE)
@Type(JsonType.class)
@Column(name = "emailaddresses")
private Map<String, String> emailAddresses = new HashMap<>();
@Transient
private PatchableMapWrapper<String> emailAddressesWrapper;
@Builder.Default
@Setter(AccessLevel.NONE)
@Type(JsonType.class)
@Column(name = "phonenumbers")
private Map<String, String> phoneNumbers = new HashMap<>();
@Transient
private PatchableMapWrapper<String> phoneNumbersWrapper;
public PatchableMapWrapper<String> getEmailAddresses() {
return PatchableMapWrapper.of(
emailAddressesWrapper,
(newWrapper) -> {emailAddressesWrapper = newWrapper;},
emailAddresses);
}
public void putEmailAddresses(Map<String, String> newEmailAddresses) {
getEmailAddresses().assign(newEmailAddresses);
}
public PatchableMapWrapper<String> getPhoneNumbers() {
return PatchableMapWrapper.of(phoneNumbersWrapper, (newWrapper) -> {phoneNumbersWrapper = newWrapper;}, phoneNumbers);
}
public void putPhoneNumbers(Map<String, String> newPhoneNumbers) {
getPhoneNumbers().assign(newPhoneNumbers);
}
@Override
public String toString() {
return toString.apply(this);
}
@Override
public String toShortString() {
return caption;
}
}

View File

@ -29,7 +29,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
private Mapper mapper;
@Autowired
private HsOfficeContactRepository contactRepo;
private HsOfficeContactRbacRepository contactRepo;
@Override
@Transactional(readOnly = true)
@ -54,7 +54,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
context.define(currentUser, assumedRoles);
final var entityToSave = mapper.map(body, HsOfficeContactEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
final var saved = contactRepo.save(entityToSave);
@ -119,7 +119,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
}
@SuppressWarnings("unchecked")
final BiConsumer<HsOfficeContactInsertResource, HsOfficeContactEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
final BiConsumer<HsOfficeContactInsertResource, HsOfficeContactRbacEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
entity.putEmailAddresses(from(resource.getEmailAddresses()));
entity.putPhoneNumbers(from(resource.getPhoneNumbers()));
};

View File

@ -1,123 +0,0 @@
package net.hostsharing.hsadminng.hs.office.contact;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
import jakarta.persistence.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@Table(name = "hs_office_contact_rv")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@DisplayName("Contact")
public class HsOfficeContactEntity implements Stringifyable, RbacObject<HsOfficeContactEntity> {
private static Stringify<HsOfficeContactEntity> toString = stringify(HsOfficeContactEntity.class, "contact")
.withProp(Fields.caption, HsOfficeContactEntity::getCaption)
.withProp(Fields.emailAddresses, HsOfficeContactEntity::getEmailAddresses);
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid;
@Version
private int version;
@Column(name = "caption")
private String caption;
@Column(name = "postaladdress")
private String postalAddress; // multiline free-format text
@Builder.Default
@Setter(AccessLevel.NONE)
@Type(JsonType.class)
@Column(name = "emailaddresses")
private Map<String, String> emailAddresses = new HashMap<>();
@Transient
private PatchableMapWrapper<String> emailAddressesWrapper;
@Builder.Default
@Setter(AccessLevel.NONE)
@Type(JsonType.class)
@Column(name = "phonenumbers")
private Map<String, String> phoneNumbers = new HashMap<>();
@Transient
private PatchableMapWrapper<String> phoneNumbersWrapper;
public PatchableMapWrapper<String> getEmailAddresses() {
return PatchableMapWrapper.of(emailAddressesWrapper, (newWrapper) -> {emailAddressesWrapper = newWrapper; }, emailAddresses );
}
public void putEmailAddresses(Map<String, String> newEmailAddresses) {
getEmailAddresses().assign(newEmailAddresses);
}
public PatchableMapWrapper<String> getPhoneNumbers() {
return PatchableMapWrapper.of(phoneNumbersWrapper, (newWrapper) -> {phoneNumbersWrapper = newWrapper; }, phoneNumbers );
}
public void putPhoneNumbers(Map<String, String> newPhoneNumbers) {
getPhoneNumbers().assign(newPhoneNumbers);
}
@Override
public String toString() {
return toString.apply(this);
}
@Override
public String toShortString() {
return caption;
}
public static RbacView rbac() {
return rbacViewFor("contact", HsOfficeContactEntity.class)
.withIdentityView(SQL.projection("caption"))
.withUpdatableColumns("caption", "postalAddress", "emailAddresses", "phoneNumbers")
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
with.permission(DELETE);
})
.createSubRole(ADMIN, (with) -> {
with.permission(UPDATE);
})
.createSubRole(REFERRER, (with) -> {
with.permission(SELECT);
})
.toRole(GLOBAL, GUEST).grantPermission(INSERT);
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("5-hs-office/501-contact/5013-hs-office-contact-rbac");
}
}

View File

@ -9,9 +9,9 @@ import java.util.Optional;
class HsOfficeContactEntityPatcher implements EntityPatcher<HsOfficeContactPatchResource> {
private final HsOfficeContactEntity entity;
private final HsOfficeContactRbacEntity entity;
HsOfficeContactEntityPatcher(final HsOfficeContactEntity entity) {
HsOfficeContactEntityPatcher(final HsOfficeContactRbacEntity entity) {
this.entity = entity;
}

View File

@ -0,0 +1,48 @@
package net.hostsharing.hsadminng.hs.office.contact;
import lombok.*;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.*;
import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_office_contact_rv")
@Getter
@Setter
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@DisplayAs("RbacContact")
public class HsOfficeContactRbacEntity extends HsOfficeContact {
public static RbacView rbac() {
return rbacViewFor("contact", HsOfficeContactRbacEntity.class)
.withIdentityView(SQL.projection("caption"))
.withUpdatableColumns("caption", "postalAddress", "emailAddresses", "phoneNumbers")
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
with.permission(DELETE);
})
.createSubRole(ADMIN, (with) -> {
with.permission(UPDATE);
})
.createSubRole(REFERRER, (with) -> {
with.permission(SELECT);
})
.toRole(GLOBAL, GUEST).grantPermission(INSERT);
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("5-hs-office/501-contact/5013-hs-office-contact-rbac");
}
}

View File

@ -7,18 +7,18 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface HsOfficeContactRepository extends Repository<HsOfficeContactEntity, UUID> {
public interface HsOfficeContactRbacRepository extends Repository<HsOfficeContactRbacEntity, UUID> {
Optional<HsOfficeContactEntity> findByUuid(UUID id);
Optional<HsOfficeContactRbacEntity> findByUuid(UUID id);
@Query("""
SELECT c FROM HsOfficeContactEntity c
SELECT c FROM HsOfficeContactRbacEntity c
WHERE :caption is null
OR c.caption like concat(cast(:caption as text), '%')
""")
List<HsOfficeContactEntity> findContactByOptionalCaptionLike(String caption);
List<HsOfficeContactRbacEntity> findContactByOptionalCaptionLike(String caption);
HsOfficeContactEntity save(final HsOfficeContactEntity entity);
HsOfficeContactRbacEntity save(final HsOfficeContactRbacEntity entity);
int deleteByUuid(final UUID uuid);

View File

@ -0,0 +1,21 @@
package net.hostsharing.hsadminng.hs.office.contact;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Entity
@Table(name = "hs_office_contact")
@Getter
@Setter
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@DisplayAs("RealContact")
public class HsOfficeContactRealEntity extends HsOfficeContact {
}

View File

@ -0,0 +1,26 @@
package net.hostsharing.hsadminng.hs.office.contact;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface HsOfficeContactRealRepository extends Repository<HsOfficeContactRealEntity, UUID> {
Optional<HsOfficeContactRealEntity> findByUuid(UUID id);
@Query("""
SELECT c FROM HsOfficeContactRealEntity c
WHERE :caption is null
OR c.caption like concat(cast(:caption as text), '%')
""")
List<HsOfficeContactRealEntity> findContactByOptionalCaptionLike(String caption);
HsOfficeContactRealEntity save(final HsOfficeContactRealEntity entity);
int deleteByUuid(final UUID uuid);
long count();
}

View File

@ -6,9 +6,9 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -40,8 +40,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopAssetsTransaction")
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacObject<HsOfficeCoopAssetsTransactionEntity> {
@DisplayAs("CoopAssetsTransaction")
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseEntity<HsOfficeCoopAssetsTransactionEntity> {
private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
.withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)
@ -107,7 +107,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
@Override
public HsOfficeCoopAssetsTransactionEntity load() {
RbacObject.super.load();
BaseEntity.super.load();
membership.load();
return this;
}

View File

@ -5,10 +5,10 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -38,8 +38,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject<HsOfficeCoopSharesTransactionEntity> {
@DisplayAs("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseEntity<HsOfficeCoopSharesTransactionEntity> {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withIdProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged)
@ -104,7 +104,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
@Override
public HsOfficeCoopSharesTransactionEntity load() {
RbacObject.super.load();
BaseEntity.super.load();
membership.load();
return this;
}

View File

@ -5,9 +5,10 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeDebitors
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import org.apache.commons.lang3.Validate;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
@ -17,11 +18,12 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import jakarta.validation.ValidationException;
import java.util.List;
import java.util.UUID;
import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
@RestController
@ -38,7 +40,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
private HsOfficeDebitorRepository debitorRepo;
@Autowired
private HsOfficeRelationRepository relRepo;
private HsOfficeRelationRealRepository relrealRepo;
@PersistenceContext
private EntityManager em;
@ -82,13 +84,16 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
if ( body.getDebitorRel() != null ) {
body.getDebitorRel().setType(DEBITOR.name());
final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationEntity.class);
entityToSave.setDebitorRel(relRepo.save(debitorRel));
final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationRealEntity.class);
validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
entityToSave.setDebitorRel(relrealRepo.save(debitorRel));
} else {
final var debitorRelOptional = relRepo.findByUuid(body.getDebitorRelUuid());
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
debitorRelOptional.ifPresentOrElse(
debitorRel -> {entityToSave.setDebitorRel(relRepo.save(debitorRel));},
() -> { throw new EntityNotFoundException("ERROR: [400] debitorRelUuid not found: " + body.getDebitorRelUuid());});
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
() -> { throw new ValidationException("Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());});
}
final var savedEntity = debitorRepo.save(entityToSave);
@ -155,4 +160,15 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
return ResponseEntity.ok(mapped);
}
// TODO.impl: extract this to some generally usable class?
private <T extends BaseEntity<T>> T validateEntityExists(final String property, final T entitySkeleton) {
final var foundEntity = em.find(entitySkeleton.getClass(), entitySkeleton.getUuid());
if ( foundEntity == null) {
throw new ValidationException("Unable to find " + DisplayName.of(entitySkeleton) + " by " + property + ": " + entitySkeleton.getUuid());
}
//noinspection unchecked
return (T) foundEntity;
}
}

View File

@ -5,11 +5,13 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@ -57,8 +59,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Debitor")
public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>, Stringifyable {
@DisplayAs("Debitor")
public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>, Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
@ -66,7 +68,7 @@ public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>,
private static Stringify<HsOfficeDebitorEntity> stringify =
stringify(HsOfficeDebitorEntity.class, "debitor")
.withIdProp(HsOfficeDebitorEntity::toShortString)
.withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelationEntity::toShortString).orElse(null))
.withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelation::toShortString).orElse(null))
.withProp(HsOfficeDebitorEntity::getDefaultPrefix)
.quotedValues(false);
@ -101,7 +103,7 @@ public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>,
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "debitorreluuid", nullable = false)
private HsOfficeRelationEntity debitorRel;
private HsOfficeRelationRealEntity debitorRel;
@Column(name = "billable", nullable = false)
private Boolean billable; // not a primitive because otherwise the default would be false
@ -128,7 +130,7 @@ public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>,
@Override
public HsOfficeDebitorEntity load() {
RbacObject.super.load();
BaseEntity.super.load();
if (partner != null) {
partner.load();
}
@ -188,7 +190,7 @@ public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>,
"defaultPrefix")
.toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, usingCase(DEBITOR),
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationRbacEntity.class, usingCase(DEBITOR),
directlyFetchedByDependsOnColumn(),
dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
@ -202,7 +204,7 @@ public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>,
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, usingDefaultCase(),
.importEntityAlias("partnerRel", HsOfficeRelationRbacEntity.class, usingDefaultCase(),
dependsOnColumn("debitorRelUuid"),
fetchedBySql("""
SELECT ${columns}

View File

@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
@ -25,7 +25,7 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher<HsOfficeDebitorPatch
public void apply(final HsOfficeDebitorPatchResource resource) {
OptionalFromJson.of(resource.getDebitorRelUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "debitorRel");
entity.setDebitorRel(em.getReference(HsOfficeRelationEntity.class, newValue));
entity.setDebitorRel(em.getReference(HsOfficeRelationRealEntity.class, newValue));
});
Optional.ofNullable(resource.getBillable()).ifPresent(entity::setBillable);
OptionalFromJson.of(resource.getVatId()).ifPresent(entity::setVatId);

View File

@ -33,7 +33,7 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
JOIN HsOfficePersonEntity person
ON person.uuid = partner.partnerRel.holder.uuid
OR person.uuid = debitor.debitorRel.holder.uuid
JOIN HsOfficeContactEntity contact
JOIN HsOfficeContactRealEntity contact
ON contact.uuid = debitor.debitorRel.contact.uuid
OR contact.uuid = partner.partnerRel.contact.uuid
WHERE :name is null

View File

@ -7,9 +7,9 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@ -61,8 +61,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Membership")
public class HsOfficeMembershipEntity implements RbacObject<HsOfficeMembershipEntity>, Stringifyable {
@DisplayAs("Membership")
public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEntity>, Stringifyable {
public static final String MEMBER_NUMBER_TAG = "M-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
@ -102,7 +102,7 @@ public class HsOfficeMembershipEntity implements RbacObject<HsOfficeMembershipEn
@Override
public HsOfficeMembershipEntity load() {
RbacObject.super.load();
BaseEntity.super.load();
partner.load();
return this;
}
@ -165,7 +165,7 @@ public class HsOfficeMembershipEntity implements RbacObject<HsOfficeMembershipEn
.withRestrictedViewOrderBy(SQL.projection("validity"))
.withUpdatableColumns("validity", "membershipFeeBillable", "status")
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, usingDefaultCase(),
.importEntityAlias("partnerRel", HsOfficeRelationRbacEntity.class, usingDefaultCase(),
dependsOnColumn("partnerUuid"),
fetchedBySql("""
SELECT ${columns}

View File

@ -2,18 +2,18 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.errors.ReferenceNotFoundException;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartnersApi;
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.HsOfficePartnerResource;
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.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -42,7 +42,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerRepository partnerRepo;
@Autowired
private HsOfficeRelationRepository relationRepo;
private HsOfficeRelationRealRepository relationRepo;
@PersistenceContext
private EntityManager em;
@ -141,7 +141,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
return ResponseEntity.ok(mapped);
}
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationEntity previousPartnerRel) {
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
}
@ -155,17 +155,17 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
return entityToSave;
}
private HsOfficeRelationEntity persistPartnerRel(final HsOfficePartnerRelInsertResource resource) {
final var entity = new HsOfficeRelationEntity();
private HsOfficeRelationRealEntity persistPartnerRel(final HsOfficePartnerRelInsertResource resource) {
final var entity = new HsOfficeRelationRealEntity();
entity.setType(HsOfficeRelationType.PARTNER);
entity.setAnchor(ref(HsOfficePersonEntity.class, resource.getAnchorUuid()));
entity.setHolder(ref(HsOfficePersonEntity.class, resource.getHolderUuid()));
entity.setContact(ref(HsOfficeContactEntity.class, resource.getContactUuid()));
entity.setContact(ref(HsOfficeContactRealEntity.class, resource.getContactUuid()));
em.persist(entity);
return entity;
}
private <E extends RbacObject> E ref(final Class<E> entityClass, final UUID uuid) {
private <E extends BaseEntity> E ref(final Class<E> entityClass, final UUID uuid) {
try {
return em.getReference(entityClass, uuid);
} catch (final Throwable exc) {