Merge branch 'master' into TP-20250113-list-subscription-view

This commit is contained in:
Dev und Test fuer hsadminng 2025-01-16 09:51:33 +01:00
commit 02495de36f
111 changed files with 1095 additions and 758 deletions

View File

@ -93,7 +93,8 @@ alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResource
alias gw-test='. .aliases; ./gradlew test' alias gw-test='. .aliases; ./gradlew test'
alias gw-check='. .aliases; gw test check -x pitest' alias gw-check='. .aliases; gw test check -x pitest'
alias cas-curl='bin/cas-curl' alias howto=bin/howto
alias cas-curl=bin/cas-curl
# etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries # etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries
alias gw-importOfficeData-in-docker-compose=' alias gw-importOfficeData-in-docker-compose='
@ -108,3 +109,4 @@ source .environment
alias scenario-reports-upload='./gradlew scenarioTests convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office' alias scenario-reports-upload='./gradlew scenarioTests convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office'
alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office' alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office'

View File

@ -4,5 +4,5 @@ export HSADMINNG_POSTGRES_ADMIN_PASSWORD=
export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted
export HSADMINNG_SUPERUSER=superuser-alex@hostsharing.net export HSADMINNG_SUPERUSER=superuser-alex@hostsharing.net
export HSADMINNG_MIGRATION_DATA_PATH=migration export HSADMINNG_MIGRATION_DATA_PATH=migration
export LIQUIBASE_CONTEXT= export LIQUIBASE_COMMAND_CONTEXT_FILTER=
export LANG=en_US.UTF-8 export LANG=en_US.UTF-8

View File

@ -4,5 +4,5 @@ unset HSADMINNG_POSTGRES_ADMIN_PASSWORD
unset HSADMINNG_POSTGRES_RESTRICTED_USERNAME unset HSADMINNG_POSTGRES_RESTRICTED_USERNAME
unset HSADMINNG_SUPERUSER unset HSADMINNG_SUPERUSER
unset HSADMINNG_MIGRATION_DATA_PATH unset HSADMINNG_MIGRATION_DATA_PATH
unset LIQUIBASE_CONTEXT unset LIQUIBASE_COMMAND_CONTEXT_FILTER

View File

@ -617,6 +617,11 @@ Besides the following *How Tos* you can also find several *How Tos* in the sourc
grep -r HOWTO src grep -r HOWTO src
``` ```
also try this (assumed you've sourced .aliases):
```sh
howto
```
### How to Configure .pgpass for the Default PostgreSQL Database? ### How to Configure .pgpass for the Default PostgreSQL Database?
To access the default database schema as used during development, add this line to your `.pgpass` file in your users home directory: To access the default database schema as used during development, add this line to your `.pgpass` file in your users home directory:

86
bin/howto Executable file
View File

@ -0,0 +1,86 @@
#!/usr/bin/python3
import os
import sys
from urllib.parse import urljoin, quote
def path_to_file_uri(path):
"""
Converts a file path to a file URI.
Handles absolute and relative paths.
"""
abs_path = os.path.abspath(path)
return urljoin("file://", quote(abs_path))
def is_binary_file(filepath, chunk_size=1024):
"""
Prüft, ob eine Datei binär ist, indem sie den Inhalt der Datei auf nicht-druckbare Zeichen untersucht.
"""
try:
with open(filepath, "rb") as file:
chunk = file.read(chunk_size)
if b"\0" in chunk: # Nullbyte ist ein typisches Zeichen für Binärdateien
return True
return False
except Exception as e:
print(f"Fehler beim Prüfen, ob Datei binär ist: {filepath}: {e}")
return True
def search_keywords_in_files(keywords):
if not keywords:
print("Bitte geben Sie mindestens ein Stichwort an.")
sys.exit(1)
# Allowed comment symbols
comment_symbols = {"//", "#", ";"}
for root, dirs, files in os.walk("."):
# Ausschließen bestimmter Verzeichnisse
dirs[:] = [d for d in dirs if d not in {".git", "build"}]
for file in files:
filepath = os.path.join(root, file)
# Überspringen von Binärdateien
if is_binary_file(filepath):
continue
try:
with open(filepath, "r", encoding="utf-8") as f:
lines = f.readlines()
for line_number, line in enumerate(lines, start=1):
stripped_line = line.lstrip() # Entfernt führende Leerzeichen
for symbol in comment_symbols:
if stripped_line.startswith(symbol):
# Entfernt das Kommentarzeichen und nachfolgende Leerzeichen
howtoMatch = stripped_line[len(symbol):].lstrip()
if howtoMatch.startswith(("HOWTO ", "HOWTO: ", "How to ")):
if all(keyword in howtoMatch.lower() for keyword in keywords):
# Titelzeile ohne Kommentarzeichen
print(howtoMatch.rstrip())
# Ausgabe nachfolgender Zeilen mit dem gleichen Kommentar-Präfix
for subsequent_line in lines[line_number:]:
subsequent_line = subsequent_line.lstrip()
if subsequent_line.startswith(symbol):
# Entfernt Kommentarzeichen aus Folgezeilen
print("\t" + subsequent_line[len(symbol):].strip())
else:
break
# Link mit Zeilennummer
print(f"--> {path_to_file_uri(filepath)}:{line_number}")
# Abstand zwischen Matches
print()
break
except Exception as e:
print(f"Fehler beim Lesen der Datei {filepath}: {e}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Verwendung: bin/howto <Stichwort1> <Stichwort2> ...")
sys.exit(1)
search_keywords_in_files([arg.lower() for arg in sys.argv[1:]])

View File

@ -1,17 +1,20 @@
plugins { plugins {
id 'java' id 'java'
id 'org.springframework.boot' version '3.3.7' id 'org.springframework.boot' version '3.4.1'
id 'io.spring.dependency-management' version '1.1.7' id 'io.spring.dependency-management' version '1.1.7'
id 'io.openapiprocessor.openapi-processor' version '2023.2' id 'io.openapiprocessor.openapi-processor' version '2023.2'
id 'com.github.jk1.dependency-license-report' version '2.9' id 'com.github.jk1.dependency-license-report' version '2.9'
id "org.owasp.dependencycheck" version "11.1.1" id "org.owasp.dependencycheck" version "12.0.0"
id "com.diffplug.spotless" version "7.0.0" id "com.diffplug.spotless" version "7.0.2"
id 'jacoco' id 'jacoco'
id 'info.solidsoft.pitest' version '1.15.0' id 'info.solidsoft.pitest' version '1.15.0'
id 'se.patrikerdes.use-latest-versions' version '0.2.18' id 'se.patrikerdes.use-latest-versions' version '0.2.18'
id 'com.github.ben-manes.versions' version '0.51.0' id 'com.github.ben-manes.versions' version '0.51.0'
} }
// HOWTO: find out which dependency versions are managed by Spring Boot:
// https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html
group = 'net.hostsharing' group = 'net.hostsharing'
version = '0.0.1-SNAPSHOT' version = '0.0.1-SNAPSHOT'
@ -20,6 +23,9 @@ wrapper {
gradleVersion = '8.5' gradleVersion = '8.5'
} }
// TODO.impl: self-attaching is deprecated, see:
// https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3
configurations { configurations {
compileOnly { compileOnly {
extendsFrom annotationProcessor extendsFrom annotationProcessor
@ -61,23 +67,23 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0' implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0'
implementation 'org.springdoc:springdoc-openapi:2.6.0' implementation 'org.springdoc:springdoc-openapi:2.8.3'
implementation 'org.postgresql:postgresql:42.7.4' implementation 'org.postgresql:postgresql'
implementation 'org.liquibase:liquibase-core:4.30.0' implementation 'org.liquibase:liquibase-core'
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0' implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
implementation 'org.openapitools:jackson-databind-nullable:0.2.6' implementation 'org.openapitools:jackson-databind-nullable:0.2.6'
implementation 'org.apache.commons:commons-text:1.13.0' implementation 'org.apache.commons:commons-text:1.13.0'
implementation 'net.java.dev.jna:jna:5.16.0' implementation 'net.java.dev.jna:jna:5.16.0'
implementation 'org.modelmapper:modelmapper:3.2.2' implementation 'org.modelmapper:modelmapper:3.2.2'
implementation 'org.iban4j:iban4j:3.2.10-RELEASE' implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.3'
implementation 'org.reflections:reflections:0.10.2' implementation 'org.reflections:reflections:0.10.2'
compileOnly 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok' testCompileOnly 'org.projectlombok:lombok'
// FIXME: developmentOnly 'org.springframework.boot:spring-boot-devtools' developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok'
@ -89,7 +95,7 @@ dependencies {
testImplementation 'org.testcontainers:postgresql' testImplementation 'org.testcontainers:postgresql'
testImplementation 'com.tngtech.archunit:archunit-junit5:1.3.0' testImplementation 'com.tngtech.archunit:archunit-junit5:1.3.0'
testImplementation 'io.rest-assured:spring-mock-mvc' testImplementation 'io.rest-assured:spring-mock-mvc'
testImplementation 'org.hamcrest:hamcrest-core:3.0' testImplementation 'org.hamcrest:hamcrest-core'
testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1' testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api' testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'org.wiremock:wiremock-standalone:3.10.0' testImplementation 'org.wiremock:wiremock-standalone:3.10.0'

View File

@ -5,9 +5,23 @@
{ "moduleLicense": "Apache-2.0" }, { "moduleLicense": "Apache-2.0" },
{ "moduleLicense": "Apache License 2.0" }, { "moduleLicense": "Apache License 2.0" },
{ "moduleLicense": "Apache License v2.0" }, { "moduleLicense": "Apache License v2.0" },
{ "moduleLicense": "Apache License Version 2.0" },
{ "moduleLicense": "Apache License, Version 2.0" }, { "moduleLicense": "Apache License, Version 2.0" },
{ "moduleLicense": "The Apache License, Version 2.0" },
{ "moduleLicense": "The Apache Software License, Version 2.0" }, { "moduleLicense": "The Apache Software License, Version 2.0" },
{
"moduleLicense": null,
"#moduleLicense": "Apache License 2.0, see https://github.com/springdoc/springdoc-openapi/blob/main/LICENSE",
"moduleVersion": "2.4.0",
"moduleName": "org.springdoc:springdoc-openapi"
},
{
"moduleLicense": null,
"moduleVersion": "1.0.0",
"moduleName": "org.jspecify:jspecify"
},
{ "moduleLicense": "BSD License" }, { "moduleLicense": "BSD License" },
{ "moduleLicense": "BSD-2-Clause" }, { "moduleLicense": "BSD-2-Clause" },
{ "moduleLicense": "BSD-3-Clause" }, { "moduleLicense": "BSD-3-Clause" },
@ -46,14 +60,8 @@
{ {
"moduleLicense": "Public Domain, per Creative Commons CC0", "moduleLicense": "Public Domain, per Creative Commons CC0",
"moduleVersion": "2.0.3" "moduleVersion": "2.0.3"
},
{
"moduleLicense": null,
"#moduleLicense": "Apache License 2.0, see https://github.com/springdoc/springdoc-openapi/blob/main/LICENSE",
"moduleVersion": "2.4.0",
"moduleName": "org.springdoc:springdoc-openapi"
} }
] ]
} }

View File

@ -97,6 +97,7 @@ public class RestResponseEntityExceptionHandler
return errorResponse(request, HttpStatus.valueOf(statusCode.value()), return errorResponse(request, HttpStatus.valueOf(statusCode.value()),
Optional.ofNullable(response.getBody()).map(Object::toString).orElse(firstMessageLine(exc))); Optional.ofNullable(response.getBody()).map(Object::toString).orElse(firstMessageLine(exc)));
} }
@Override @Override
@SuppressWarnings("unchecked,rawtypes") @SuppressWarnings("unchecked,rawtypes")
protected ResponseEntity handleHttpMessageNotReadable( protected ResponseEntity handleHttpMessageNotReadable(
@ -131,7 +132,7 @@ public class RestResponseEntityExceptionHandler
final HttpStatusCode status, final HttpStatusCode status,
final WebRequest request) { final WebRequest request) {
final var errorList = exc final var errorList = exc
.getAllValidationResults() .getParameterValidationResults()
.stream() .stream()
.map(ParameterValidationResult::getResolvableErrors) .map(ParameterValidationResult::getResolvableErrors)
.flatMap(Collection::stream) .flatMap(Collection::stream)

View File

@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.role.WithRoleId;
import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable; import net.hostsharing.hsadminng.repr.Stringifyable;
@ -24,7 +25,7 @@ import static net.hostsharing.hsadminng.repr.Stringify.stringify;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@DisplayAs("BookingDebitor") @DisplayAs("BookingDebitor")
public class HsBookingDebitorEntity implements Stringifyable { public class HsBookingDebitorEntity implements Stringifyable, WithRoleId {
public static final String DEBITOR_NUMBER_TAG = "D-"; public static final String DEBITOR_NUMBER_TAG = "D-";

View File

@ -7,7 +7,7 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjec
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectResource;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -26,7 +26,7 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsBookingProjectRbacRepository bookingProjectRepo; private HsBookingProjectRbacRepository bookingProjectRepo;

View File

@ -89,7 +89,7 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity<HsHost
@JoinColumn(name = "alarmcontactuuid") @JoinColumn(name = "alarmcontactuuid")
private HsOfficeContactRealEntity alarmContact; private HsOfficeContactRealEntity alarmContact;
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY) @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid") @JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
private List<HsHostingAssetRealEntity> subHostingAssets; private List<HsHostingAssetRealEntity> subHostingAssets;

View File

@ -12,7 +12,7 @@ import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAsse
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource;
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.KeyValueMap;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -36,7 +36,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsHostingAssetRbacRepository rbacAssetRepo; private HsHostingAssetRbacRepository rbacAssetRepo;

View File

@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.lambda.Reducer; import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.mapper.ToStringConverter; import net.hostsharing.hsadminng.mapper.ToStringConverter;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
@ -31,8 +31,8 @@ public class DomainSetupHostingAssetFactory extends HostingAssetFactory {
final EntityManagerWrapper emw, final EntityManagerWrapper emw,
final HsBookingItemRealEntity newBookingItemRealEntity, final HsBookingItemRealEntity newBookingItemRealEntity,
final HsHostingAssetAutoInsertResource asset, final HsHostingAssetAutoInsertResource asset,
final StandardMapper standardMapper) { final StrictMapper StrictMapper) {
super(emw, newBookingItemRealEntity, asset, standardMapper); super(emw, newBookingItemRealEntity, asset, StrictMapper);
} }
@Override @Override

View File

@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAsse
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
@ -16,7 +16,7 @@ abstract class HostingAssetFactory {
final EntityManagerWrapper emw; final EntityManagerWrapper emw;
final HsBookingItemRealEntity fromBookingItem; final HsBookingItemRealEntity fromBookingItem;
final HsHostingAssetAutoInsertResource asset; final HsHostingAssetAutoInsertResource asset;
final StandardMapper standardMapper; final StrictMapper StrictMapper;
protected abstract HsHostingAsset create(); protected abstract HsHostingAsset create();

View File

@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAsse
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent; import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
@ -25,7 +25,7 @@ public class HsBookingItemCreatedListener implements ApplicationListener<Booking
private ObjectMapper jsonMapper; private ObjectMapper jsonMapper;
@Autowired @Autowired
private StandardMapper standardMapper; private StrictMapper StrictMapper;
@Override @Override
@SneakyThrows @SneakyThrows
@ -44,9 +44,9 @@ public class HsBookingItemCreatedListener implements ApplicationListener<Booking
final var asset = jsonMapper.readValue(event.getEntity().getAssetJson(), HsHostingAssetAutoInsertResource.class); final var asset = jsonMapper.readValue(event.getEntity().getAssetJson(), HsHostingAssetAutoInsertResource.class);
final var factory = switch (newBookingItemRealEntity.getType()) { final var factory = switch (newBookingItemRealEntity.getType()) {
case PRIVATE_CLOUD, CLOUD_SERVER, MANAGED_SERVER -> case PRIVATE_CLOUD, CLOUD_SERVER, MANAGED_SERVER ->
forNowNoAutomaticHostingAssetCreationPossible(emw, newBookingItemRealEntity, asset, standardMapper); forNowNoAutomaticHostingAssetCreationPossible(emw, newBookingItemRealEntity, asset, StrictMapper);
case MANAGED_WEBSPACE -> new ManagedWebspaceHostingAssetFactory(emw, newBookingItemRealEntity, asset, standardMapper); case MANAGED_WEBSPACE -> new ManagedWebspaceHostingAssetFactory(emw, newBookingItemRealEntity, asset, StrictMapper);
case DOMAIN_SETUP -> new DomainSetupHostingAssetFactory(emw, newBookingItemRealEntity, asset, standardMapper); case DOMAIN_SETUP -> new DomainSetupHostingAssetFactory(emw, newBookingItemRealEntity, asset, StrictMapper);
}; };
if (factory != null) { if (factory != null) {
final var statusMessage = factory.createAndPersist(); final var statusMessage = factory.createAndPersist();
@ -62,9 +62,9 @@ public class HsBookingItemCreatedListener implements ApplicationListener<Booking
final EntityManagerWrapper emw, final EntityManagerWrapper emw,
final HsBookingItemRealEntity fromBookingItem, final HsBookingItemRealEntity fromBookingItem,
final HsHostingAssetAutoInsertResource asset, final HsHostingAssetAutoInsertResource asset,
final StandardMapper standardMapper final StrictMapper StrictMapper
) { ) {
return new HostingAssetFactory(emw, fromBookingItem, asset, standardMapper) { return new HostingAssetFactory(emw, fromBookingItem, asset, StrictMapper) {
@Override @Override
protected HsHostingAsset create() { protected HsHostingAsset create() {

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAsse
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
@ -19,8 +19,8 @@ public class ManagedWebspaceHostingAssetFactory extends HostingAssetFactory {
final EntityManagerWrapper emw, final EntityManagerWrapper emw,
final HsBookingItemRealEntity newBookingItemRealEntity, final HsBookingItemRealEntity newBookingItemRealEntity,
final HsHostingAssetAutoInsertResource asset, final HsHostingAssetAutoInsertResource asset,
final StandardMapper standardMapper) { final StrictMapper StrictMapper) {
super(emw, newBookingItemRealEntity, asset, standardMapper); super(emw, newBookingItemRealEntity, asset, StrictMapper);
} }
@Override @Override
@ -32,7 +32,7 @@ public class ManagedWebspaceHostingAssetFactory extends HostingAssetFactory {
.map(Enum::name) .map(Enum::name)
.orElse(null)); .orElse(null));
} }
final var managedWebspaceHostingAsset = standardMapper.map(asset, HsHostingAssetRealEntity.class); final var managedWebspaceHostingAsset = StrictMapper.map(asset, HsHostingAssetRealEntity.class);
managedWebspaceHostingAsset.setBookingItem(fromBookingItem); managedWebspaceHostingAsset.setBookingItem(fromBookingItem);
emw.createQuery( emw.createQuery(
"SELECT asset FROM HsHostingAssetRealEntity asset WHERE asset.bookingItem.uuid=:bookingItemUuid", "SELECT asset FROM HsHostingAssetRealEntity asset WHERE asset.bookingItem.uuid=:bookingItemUuid",

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountResource;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import org.iban4j.BicUtil; import org.iban4j.BicUtil;
import org.iban4j.IbanUtil; import org.iban4j.IbanUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -25,7 +25,7 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficeBankAccountRepository bankAccountRepo; private HsOfficeBankAccountRepository bankAccountRepo;

View File

@ -12,6 +12,7 @@ import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.persistence.BaseEntity; import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.rbac.role.WithRoleId;
import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable; import net.hostsharing.hsadminng.repr.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
@ -37,7 +38,7 @@ import static net.hostsharing.hsadminng.repr.Stringify.stringify;
@SuperBuilder(toBuilder = true) @SuperBuilder(toBuilder = true)
@FieldNameConstants @FieldNameConstants
@DisplayAs("Contact") @DisplayAs("Contact")
public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContact> { public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContact>, WithRoleId {
private static Stringify<HsOfficeContact> toString = stringify(HsOfficeContact.class, "contact") private static Stringify<HsOfficeContact> toString = stringify(HsOfficeContact.class, "contact")
.withProp(Fields.caption, HsOfficeContact::getCaption) .withProp(Fields.caption, HsOfficeContact::getCaption)

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.contact; package net.hostsharing.hsadminng.hs.office.contact;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
@ -28,7 +28,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficeContactRbacRepository contactRepo; private HsOfficeContactRbacRepository contactRepo;

View File

@ -1,4 +1,3 @@
package net.hostsharing.hsadminng.hs.office.coopassets; package net.hostsharing.hsadminng.hs.office.coopassets;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -10,11 +9,22 @@ import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity; import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec; import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL;
import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable; import net.hostsharing.hsadminng.repr.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*; import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
@ -57,8 +67,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
.quotedValues(false); .quotedValues(false);
@Id @Id
@GeneratedValue(generator = "UUID") @GeneratedValue
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid; private UUID uuid;
@Version @Version
@ -122,15 +131,15 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
return this; return this;
} }
public String getTaggedMemberNumber() {
return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-???????");
}
@Override @Override
public String toString() { public String toString() {
return stringify.apply(this); return stringify.apply(this);
} }
public String getTaggedMemberNumber() {
return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-???????");
}
@Override @Override
public String toShortString() { public String toShortString() {
return "%s:%.3s:%+1.2f".formatted( return "%s:%.3s:%+1.2f".formatted(
@ -141,7 +150,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
public static RbacSpec rbac() { public static RbacSpec rbac() {
return rbacViewFor("coopAssetsTransaction", HsOfficeCoopAssetsTransactionEntity.class) return rbacViewFor("coopAssetsTransaction", HsOfficeCoopAssetsTransactionEntity.class)
.withIdentityView(RbacSpec.SQL.projection("reference")) .withIdentityView(SQL.projection("reference"))
.withUpdatableColumns("comment") .withUpdatableColumns("comment")
.importEntityAlias("membership", HsOfficeMembershipEntity.class, usingDefaultCase(), .importEntityAlias("membership", HsOfficeMembershipEntity.class, usingDefaultCase(),
dependsOnColumn("membershipUuid"), dependsOnColumn("membershipUuid"),

View File

@ -1,14 +1,13 @@
package net.hostsharing.hsadminng.hs.office.coopshares; package net.hostsharing.hsadminng.hs.office.coopshares;
import jakarta.persistence.EntityNotFoundException;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.errors.MultiValidationException;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource;
import net.hostsharing.hsadminng.errors.MultiValidationException; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.annotation.DateTimeFormat.ISO;
@ -25,6 +24,7 @@ import java.util.function.BiConsumer;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionTypeResource.CANCELLATION; import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionTypeResource.CANCELLATION;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionTypeResource.SUBSCRIPTION; import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionTypeResource.SUBSCRIPTION;
import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
@RestController @RestController
public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopSharesApi { public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopSharesApi {
@ -33,14 +33,16 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo; private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
@Autowired
private HsOfficeMembershipRepository membershipRepo;
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
@Timed("app.office.coopShares.api.getListOfCoopShares") @Timed("app.office.coopShares.api.getListOfCoopShares")
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> getListOfCoopShares( public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> getListOfCoopShares(
final String currentSubject, final String currentSubject,
@ -55,7 +57,10 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
fromValueDate, fromValueDate,
toValueDate); toValueDate);
final var resources = mapper.mapList(entities, HsOfficeCoopSharesTransactionResource.class); final var resources = mapper.mapList(
entities,
HsOfficeCoopSharesTransactionResource.class,
ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(resources); return ResponseEntity.ok(resources);
} }
@ -70,7 +75,10 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
validate(requestBody); validate(requestBody);
final var entityToSave = mapper.map(requestBody, HsOfficeCoopSharesTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var entityToSave = mapper.map(
requestBody,
HsOfficeCoopSharesTransactionEntity.class,
RESOURCE_TO_ENTITY_POSTMAPPER);
final var saved = coopSharesTransactionRepo.save(entityToSave); final var saved = coopSharesTransactionRepo.save(entityToSave);
@ -79,7 +87,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
.path("/api/hs/office/coopsharestransactions/{id}") .path("/api/hs/office/coopsharestransactions/{id}")
.buildAndExpand(saved.getUuid()) .buildAndExpand(saved.getUuid())
.toUri(); .toUri();
final var mapped = mapper.map(saved, HsOfficeCoopSharesTransactionResource.class); final var mapped = mapper.map(saved, HsOfficeCoopSharesTransactionResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.created(uri).body(mapped); return ResponseEntity.created(uri).body(mapped);
} }
@ -95,7 +103,10 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
if (result.isEmpty()) { if (result.isEmpty()) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeCoopSharesTransactionResource.class)); return ResponseEntity.ok(mapper.map(
result.get(),
HsOfficeCoopSharesTransactionResource.class,
ENTITY_TO_RESOURCE_POSTMAPPER));
} }
@ -137,9 +148,16 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
} }
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
entity.setMembership(resolve("membership.uuid", resource.getMembershipUuid(), membershipRepo::findByUuid));
if (resource.getRevertedShareTxUuid() != null) { if (resource.getRevertedShareTxUuid() != null) {
entity.setRevertedShareTx(coopSharesTransactionRepo.findByUuid(resource.getRevertedShareTxUuid()) entity.setRevertedShareTx(resolve(
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedShareTxUuid %s not found".formatted(resource.getRevertedShareTxUuid())))); "revertedShareTx.uuid",
resource.getRevertedShareTxUuid(),
coopSharesTransactionRepo::findByUuid));
} }
}; };
final BiConsumer<HsOfficeCoopSharesTransactionEntity, HsOfficeCoopSharesTransactionResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setMembershipUuid(entity.getMembership().getUuid());
};
} }

View File

@ -7,13 +7,23 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.persistence.BaseEntity; import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL;
import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable; import net.hostsharing.hsadminng.repr.Stringifyable;
import jakarta.persistence.*; import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
@ -132,6 +142,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
NOT_NULL) NOT_NULL)
// the membership:ADMIN is not to be confused with the member itself, it's an account manager of the coop
.toRole("membership", ADMIN).grantPermission(INSERT) .toRole("membership", ADMIN).grantPermission(INSERT)
.toRole("membership", ADMIN).grantPermission(UPDATE) .toRole("membership", ADMIN).grantPermission(UPDATE)
.toRole("membership", AGENT).grantPermission(SELECT); .toRole("membership", AGENT).grantPermission(SELECT);

View File

@ -2,14 +2,16 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeDebitorsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeDebitorsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorInsertResource; 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.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityExistsValidator;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -26,6 +28,7 @@ import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag; import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
@RestController @RestController
@ -36,16 +39,22 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficeDebitorRepository debitorRepo; private HsOfficeDebitorRepository debitorRepo;
@Autowired @Autowired
private HsOfficeRelationRealRepository relrealRepo; private HsOfficeRelationRealRepository realRelRepo;
@Autowired @Autowired
private EntityExistsValidator entityValidator; private HsOfficePersonRealRepository realPersonRepo;
@Autowired
private HsOfficeContactRealRepository realContactRepo;
@Autowired
private HsOfficeBankAccountRepository bankAccountRepo;
@PersistenceContext @PersistenceContext
private EntityManager em; private EntityManager em;
@ -81,34 +90,19 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRelUuid() == null, Validate.isTrue(
body.getDebitorRel() == null || body.getDebitorRelUuid() == null,
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both"); "ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null, Validate.isTrue(
body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none"); "ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none");
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getMark() == null, Validate.isTrue(
body.getDebitorRel() == null || body.getDebitorRel().getMark() == null,
"ERROR: [400] debitorRel.mark must be null"); "ERROR: [400] debitorRel.mark must be null");
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class); final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
if (body.getDebitorRel() != null) {
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
debitorRel.setType(DEBITOR);
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
entityToSave.setDebitorRel(relrealRepo.save(debitorRel));
} else {
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
debitorRelOptional.ifPresentOrElse(
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
() -> {
throw new ValidationException(
"Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());
});
}
final var savedEntity = debitorRepo.save(entityToSave); final var savedEntity = debitorRepo.save(entityToSave).reload(em);
em.flush();
em.refresh(savedEntity);
final var uri = final var uri =
MvcUriComponentsBuilder.fromController(getClass()) MvcUriComponentsBuilder.fromController(getClass())
@ -181,7 +175,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
final var current = debitorRepo.findByUuid(debitorUuid).orElseThrow(); final var current = debitorRepo.findByUuid(debitorUuid).orElseThrow().reload(em);
new HsOfficeDebitorEntityPatcher(em, current).apply(body); new HsOfficeDebitorEntityPatcher(em, current).apply(body);
@ -191,7 +185,39 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
return ResponseEntity.ok(mapped); return ResponseEntity.ok(mapped);
} }
final BiConsumer<HsOfficeDebitorInsertResource, HsOfficeDebitorEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
if (resource.getDebitorRel() != null) {
final var debitorRel = realRelRepo.save(HsOfficeRelationRealEntity.builder()
.type(DEBITOR)
.anchor(resolve(
"debitorRel.anchor.uuid", resource.getDebitorRel().getAnchorUuid(), realPersonRepo::findByUuid))
.holder(resolve(
"debitorRel.holder.uuid", resource.getDebitorRel().getHolderUuid(), realPersonRepo::findByUuid))
.contact(resolve(
"debitorRel.contact.uuid", resource.getDebitorRel().getContactUuid(), realContactRepo::findByUuid))
.build());
entity.setDebitorRel(debitorRel);
} else {
final var debitorRelOptional = realRelRepo.findByUuid(resource.getDebitorRelUuid());
debitorRelOptional.ifPresentOrElse(
debitorRel -> {
entity.setDebitorRel(realRelRepo.save(debitorRel));
},
() -> {
throw new ValidationException(
"Unable to find debitorRel.uuid: " + resource.getDebitorRelUuid());
});
}
if (resource.getRefundBankAccountUuid() != null) {
entity.setRefundBankAccount(resolve(
"refundBankAccount.uuid", resource.getRefundBankAccountUuid(), bankAccountRepo::findByUuid));
}
};
final BiConsumer<HsOfficeDebitorEntity, HsOfficeDebitorResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { final BiConsumer<HsOfficeDebitorEntity, HsOfficeDebitorResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setDebitorNumber(entity.getTaggedDebitorNumber()); resource.setDebitorNumber(entity.getTaggedDebitorNumber());
resource.getPartner().setPartnerNumber(entity.getPartner().getTaggedPartnerNumber());
}; };
} }

View File

@ -7,7 +7,8 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartner;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
@ -16,7 +17,6 @@ import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL;
import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable; import net.hostsharing.hsadminng.repr.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.JoinFormula; import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.NotFoundAction;
@ -75,7 +75,6 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
@Id @Id
@GeneratedValue @GeneratedValue
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid; private UUID uuid;
@Version @Version
@ -87,16 +86,16 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
value = """ value = """
( (
SELECT DISTINCT partner.uuid SELECT DISTINCT partner.uuid
FROM hs_office.partner_rv partner FROM hs_office.partner partner
JOIN hs_office.relation dRel JOIN hs_office.relation dRel
ON dRel.uuid = debitorreluuid AND dRel.type = 'DEBITOR' ON dRel.uuid = debitorRelUuid AND dRel.type = 'DEBITOR'
JOIN hs_office.relation pRel JOIN hs_office.relation pRel
ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER' ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER'
WHERE pRel.holderUuid = dRel.anchorUuid WHERE pRel.holderUuid = dRel.anchorUuid
) )
""") """)
@NotFound(action = NotFoundAction.IGNORE) // TODO.impl: map a simplified raw-PartnerEntity, just for the partner-number @NotFound(action = NotFoundAction.EXCEPTION) // TODO.impl: map a simplified raw-PartnerEntity, just for the partner-number
private HsOfficePartnerEntity partner; private HsOfficePartnerRealEntity partner;
@Column(name = "debitornumbersuffix", length = 2) @Column(name = "debitornumbersuffix", length = 2)
@Pattern(regexp = TWO_DECIMAL_DIGITS) @Pattern(regexp = TWO_DECIMAL_DIGITS)
@ -132,9 +131,7 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
@Override @Override
public HsOfficeDebitorEntity load() { public HsOfficeDebitorEntity load() {
BaseEntity.super.load(); BaseEntity.super.load();
if (partner != null) {
partner.load(); partner.load();
}
debitorRel.load(); debitorRel.load();
if (refundBankAccount != null) { if (refundBankAccount != null) {
refundBankAccount.load(); refundBankAccount.load();
@ -145,7 +142,7 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
public String getTaggedDebitorNumber() { public String getTaggedDebitorNumber() {
return ofNullable(partner) return ofNullable(partner)
.filter(partner -> debitorNumberSuffix != null) .filter(partner -> debitorNumberSuffix != null)
.map(HsOfficePartnerEntity::getPartnerNumber) .map(HsOfficePartner::getPartnerNumber)
.map(partnerNumber -> DEBITOR_NUMBER_TAG + partnerNumber + debitorNumberSuffix) .map(partnerNumber -> DEBITOR_NUMBER_TAG + partnerNumber + debitorNumberSuffix)
.orElse(null); .orElse(null);
} }

View File

@ -19,7 +19,7 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
@Query(""" @Query("""
SELECT debitor FROM HsOfficeDebitorEntity debitor SELECT debitor FROM HsOfficeDebitorEntity debitor
JOIN HsOfficePartnerEntity partner JOIN HsOfficePartnerRealEntity partner
ON partner.partnerRel.holder = debitor.debitorRel.anchor ON partner.partnerRel.holder = debitor.debitorRel.anchor
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR' AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
WHERE partner.partnerNumber = :partnerNumber WHERE partner.partnerNumber = :partnerNumber
@ -42,7 +42,7 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
@Query(""" @Query("""
SELECT debitor FROM HsOfficeDebitorEntity debitor SELECT debitor FROM HsOfficeDebitorEntity debitor
JOIN HsOfficePartnerEntity partner JOIN HsOfficePartnerRealEntity partner
ON partner.partnerRel.holder = debitor.debitorRel.anchor ON partner.partnerRel.holder = debitor.debitorRel.anchor
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR' AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
JOIN HsOfficePersonRealEntity person JOIN HsOfficePersonRealEntity person

View File

@ -6,14 +6,16 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembersh
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipResource;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRbacEntity;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealRepository;
import net.hostsharing.hsadminng.mapper.StrictMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import jakarta.persistence.EntityNotFoundException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -28,7 +30,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired
private HsOfficePartnerRealRepository partnerRepo;
@Autowired @Autowired
private HsOfficeMembershipRepository membershipRepo; private HsOfficeMembershipRepository membershipRepo;
@ -47,7 +52,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
final var entities = partnerNumber != null final var entities = partnerNumber != null
? membershipRepo.findMembershipsByPartnerNumber( ? membershipRepo.findMembershipsByPartnerNumber(
cropTag(HsOfficePartnerEntity.PARTNER_NUMBER_TAG, partnerNumber)) cropTag(HsOfficePartnerRbacEntity.PARTNER_NUMBER_TAG, partnerNumber))
: partnerUuid != null : partnerUuid != null
? membershipRepo.findMembershipsByPartnerUuid(partnerUuid) ? membershipRepo.findMembershipsByPartnerUuid(partnerUuid)
: membershipRepo.findAll(); : membershipRepo.findAll();
@ -68,7 +73,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
final var entityToSave = mapper.map(body, HsOfficeMembershipEntity.class); final var entityToSave = mapper.map(body, HsOfficeMembershipEntity.class, SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER);
final var saved = membershipRepo.save(entityToSave); final var saved = membershipRepo.save(entityToSave);
@ -164,5 +169,12 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
if (entity.getValidity().hasUpperBound()) { if (entity.getValidity().hasUpperBound()) {
resource.setValidTo(entity.getValidity().upper().minusDays(1)); resource.setValidTo(entity.getValidity().upper().minusDays(1));
} }
resource.getPartner().setPartnerNumber(entity.getPartner().getTaggedPartnerNumber()); // TODO.refa: use partner mapper?
};
final BiConsumer<HsOfficeMembershipInsertResource, HsOfficeMembershipEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
entity.setPartner(partnerRepo.findByUuid(resource.getPartnerUuid())
.orElseThrow(() -> new EntityNotFoundException(
"ERROR: [400] partnerUuid %s not found".formatted(resource.getPartnerUuid()))));
}; };
} }

View File

@ -8,11 +8,12 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity; import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec; import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL;
import net.hostsharing.hsadminng.rbac.role.WithRoleId;
import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable; import net.hostsharing.hsadminng.repr.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
@ -63,7 +64,7 @@ import static net.hostsharing.hsadminng.repr.Stringify.stringify;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@DisplayAs("Membership") @DisplayAs("Membership")
public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEntity>, Stringifyable { public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEntity>, Stringifyable, WithRoleId {
public static final String MEMBER_NUMBER_TAG = "M-"; public static final String MEMBER_NUMBER_TAG = "M-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$"; public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
@ -84,7 +85,7 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "partneruuid") @JoinColumn(name = "partneruuid")
private HsOfficePartnerEntity partner; private HsOfficePartnerRealEntity partner;
@Column(name = "membernumbersuffix", length = 2) @Column(name = "membernumbersuffix", length = 2)
@Pattern(regexp = TWO_DECIMAL_DIGITS) @Pattern(regexp = TWO_DECIMAL_DIGITS)

View File

@ -2,18 +2,18 @@ package net.hostsharing.hsadminng.hs.office.membership;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.mapper.OptionalFromJson; import net.hostsharing.hsadminng.mapper.OptionalFromJson;
import net.hostsharing.hsadminng.mapper.StrictMapper;
import java.util.Optional; import java.util.Optional;
public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMembershipPatchResource> { public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMembershipPatchResource> {
private final StandardMapper mapper; private final StrictMapper mapper;
private final HsOfficeMembershipEntity entity; private final HsOfficeMembershipEntity entity;
public HsOfficeMembershipEntityPatcher( public HsOfficeMembershipEntityPatcher(
final StandardMapper mapper, final StrictMapper mapper,
final HsOfficeMembershipEntity entity) { final HsOfficeMembershipEntity entity) {
this.mapper = mapper; this.mapper = mapper;
this.entity = entity; this.entity = entity;

View File

@ -0,0 +1,103 @@
package net.hostsharing.hsadminng.hs.office.partner;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePerson;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Version;
import java.util.UUID;
import static jakarta.persistence.CascadeType.DETACH;
import static jakarta.persistence.CascadeType.MERGE;
import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.CascadeType.REFRESH;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
@MappedSuperclass
@Getter
@Setter
@SuperBuilder(toBuilder = true)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@DisplayAs("Partner")
public class HsOfficePartner<T extends HsOfficePartner<?>> implements Stringifyable, BaseEntity<T> {
public static final String PARTNER_NUMBER_TAG = "P-";
protected static Stringify<HsOfficePartner> stringify = stringify(HsOfficePartner.class, "partner")
.withIdProp(HsOfficePartner::toShortString)
.withProp(p -> ofNullable(p.getPartnerRel())
.map(HsOfficeRelation::getHolder)
.map(HsOfficePerson::toShortString)
.orElse(null))
.withProp(p -> ofNullable(p.getPartnerRel())
.map(HsOfficeRelation::getContact)
.map(HsOfficeContact::toShortString)
.orElse(null))
.quotedValues(false);
@Id
@GeneratedValue
private UUID uuid;
@Version
private int version;
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
private Integer partnerNumber;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "partnerreluuid", nullable = false)
private HsOfficeRelationRealEntity partnerRel;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "detailsuuid")
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerDetailsEntity details;
@Override
public T load() {
BaseEntity.super.load();
partnerRel.load();
if (details != null) {
details.load();
}
//noinspection unchecked
return (T) this;
}
public String getTaggedPartnerNumber() {
return PARTNER_NUMBER_TAG + partnerNumber;
}
@Override
public String toString() {
return stringify.apply(this);
}
@Override
public String toShortString() {
return getTaggedPartnerNumber();
}
}

View File

@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.BaseEntity; import net.hostsharing.hsadminng.persistence.BaseEntity;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -26,6 +26,7 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.EX_PARTNER; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.EX_PARTNER;
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag; import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
@ -38,10 +39,10 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficePartnerRepository partnerRepo; private HsOfficePartnerRbacRepository partnerRepo;
@Autowired @Autowired
private HsOfficeRelationRealRepository relationRepo; private HsOfficeRelationRealRepository relationRepo;
@ -60,7 +61,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
final var entities = partnerRepo.findPartnerByOptionalNameLike(name); final var entities = partnerRepo.findPartnerByOptionalNameLike(name);
final var resources = mapper.mapList(entities, HsOfficePartnerResource.class); final var resources = mapper.mapList(entities, HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(resources); return ResponseEntity.ok(resources);
} }
@ -83,7 +84,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
.path("/api/hs/office/partners/{id}") .path("/api/hs/office/partners/{id}")
.buildAndExpand(saved.getUuid()) .buildAndExpand(saved.getUuid())
.toUri(); .toUri();
final var mapped = mapper.map(saved, HsOfficePartnerResource.class); final var mapped = mapper.map(saved, HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.created(uri).body(mapped); return ResponseEntity.created(uri).body(mapped);
} }
@ -101,7 +102,8 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
if (result.isEmpty()) { if (result.isEmpty()) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(mapper.map(result.get(), HsOfficePartnerResource.class)); final var mapped = mapper.map(result.get(), HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(mapped);
} }
@Override @Override
@ -118,7 +120,8 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
if (result.isEmpty()) { if (result.isEmpty()) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(mapper.map(result.get(), HsOfficePartnerResource.class)); final var mapped = mapper.map(result.get(), HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(mapped);
} }
@Override @Override
@ -161,20 +164,20 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
final var saved = partnerRepo.save(current); final var saved = partnerRepo.save(current);
optionallyCreateExPartnerRelation(saved, previousPartnerRel); optionallyCreateExPartnerRelation(saved, previousPartnerRel);
final var mapped = mapper.map(saved, HsOfficePartnerResource.class); final var mapped = mapper.map(saved, HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(mapped); return ResponseEntity.ok(mapped);
} }
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) { private void optionallyCreateExPartnerRelation(final HsOfficePartnerRbacEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) { if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
// TODO.impl: we also need to use the new partner-person as the anchor // TODO.impl: we also need to use the new partner-person as the anchor
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build()); relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
} }
} }
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) { private HsOfficePartnerRbacEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
final var entityToSave = new HsOfficePartnerEntity(); final var entityToSave = new HsOfficePartnerRbacEntity();
entityToSave.setPartnerNumber(cropTag(HsOfficePartnerEntity.PARTNER_NUMBER_TAG, body.getPartnerNumber())); entityToSave.setPartnerNumber(cropTag(HsOfficePartnerRbacEntity.PARTNER_NUMBER_TAG, body.getPartnerNumber()));
entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel())); entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class)); entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
return entityToSave; return entityToSave;
@ -197,4 +200,8 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
throw new ReferenceNotFoundException(entityClass, uuid, exc); throw new ReferenceNotFoundException(entityClass, uuid, exc);
} }
} }
final BiConsumer<HsOfficePartnerRbacEntity, HsOfficePartnerResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setPartnerNumber(entity.getTaggedPartnerNumber());
};
} }

View File

@ -1,128 +0,0 @@
package net.hostsharing.hsadminng.hs.office.partner;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePerson;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL;
import net.hostsharing.hsadminng.repr.Stringify;
import net.hostsharing.hsadminng.repr.Stringifyable;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*;
import java.io.IOException;
import java.util.UUID;
import static jakarta.persistence.CascadeType.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
@Entity
@Table(schema = "hs_office", name = "partner_rv")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DisplayAs("Partner")
public class HsOfficePartnerEntity implements Stringifyable, BaseEntity<HsOfficePartnerEntity> {
public static final String PARTNER_NUMBER_TAG = "P-";
private static Stringify<HsOfficePartnerEntity> stringify = stringify(HsOfficePartnerEntity.class, "partner")
.withIdProp(HsOfficePartnerEntity::toShortString)
.withProp(p -> ofNullable(p.getPartnerRel())
.map(HsOfficeRelation::getHolder)
.map(HsOfficePerson::toShortString)
.orElse(null))
.withProp(p -> ofNullable(p.getPartnerRel())
.map(HsOfficeRelation::getContact)
.map(HsOfficeContact::toShortString)
.orElse(null))
.quotedValues(false);
@Id
@GeneratedValue
private UUID uuid;
@Version
private int version;
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
private Integer partnerNumber;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "partnerreluuid", nullable = false)
private HsOfficeRelationRealEntity partnerRel;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "detailsuuid")
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerDetailsEntity details;
@Override
public HsOfficePartnerEntity load() {
BaseEntity.super.load();
partnerRel.load();
details.load();
return this;
}
public String getTaggedPartnerNumber() {
return PARTNER_NUMBER_TAG + partnerNumber;
}
@Override
public String toString() {
return stringify.apply(this);
}
@Override
public String toShortString() {
return getTaggedPartnerNumber();
}
public static RbacSpec rbac() {
return rbacViewFor("partner", HsOfficePartnerEntity.class)
.withIdentityView(SQL.projection("'P-' || partnerNumber"))
.withUpdatableColumns("partnerRelUuid")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationRbacEntity.class,
usingDefaultCase(),
directlyFetchedByDependsOnColumn(),
dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", OWNER)
.createPermission(UPDATE).grantedTo("partnerRel", ADMIN)
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
directlyFetchedByDependsOnColumn(),
dependsOnColumn("detailsUuid"))
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", OWNER)
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
.createPermission("partnerDetails", SELECT).grantedTo("partnerRel", AGENT); // not TENANT!
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("5-hs-office/504-partner/5043-hs-office-partner-rbac");
}
}

View File

@ -9,10 +9,10 @@ import jakarta.persistence.EntityManager;
class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatchResource> { class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatchResource> {
private final EntityManager em; private final EntityManager em;
private final HsOfficePartnerEntity entity; private final HsOfficePartnerRbacEntity entity;
HsOfficePartnerEntityPatcher( HsOfficePartnerEntityPatcher(
final EntityManager em, final EntityManager em,
final HsOfficePartnerEntity entity) { final HsOfficePartnerRbacEntity entity) {
this.em = em; this.em = em;
this.entity = entity; this.entity = entity;
} }

View File

@ -0,0 +1,59 @@
package net.hostsharing.hsadminng.hs.office.partner;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL;
import jakarta.persistence.*;
import java.io.IOException;
import static jakarta.persistence.CascadeType.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor;
@Entity
@Table(schema = "hs_office", name = "partner_rv")
@Getter
@Setter
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@DisplayAs("RbacPartner")
public class HsOfficePartnerRbacEntity extends HsOfficePartner<HsOfficePartnerRbacEntity> {
public static RbacSpec rbac() {
return rbacViewFor("partner", HsOfficePartnerRbacEntity.class)
.withIdentityView(SQL.projection("'P-' || partnerNumber"))
.withUpdatableColumns("partnerRelUuid")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationRbacEntity.class,
usingDefaultCase(),
directlyFetchedByDependsOnColumn(),
dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", OWNER)
.createPermission(UPDATE).grantedTo("partnerRel", ADMIN)
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
directlyFetchedByDependsOnColumn(),
dependsOnColumn("detailsUuid"))
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", OWNER)
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
.createPermission("partnerDetails", SELECT).grantedTo("partnerRel", AGENT); // not TENANT!
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("5-hs-office/504-partner/5043-hs-office-partner-rbac");
}
}

View File

@ -0,0 +1,47 @@
package net.hostsharing.hsadminng.hs.office.partner;
import io.micrometer.core.annotation.Timed;
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 HsOfficePartnerRbacRepository extends Repository<HsOfficePartnerRbacEntity, UUID> {
@Timed("app.office.partners.repo.findByUuid.rbac")
Optional<HsOfficePartnerRbacEntity> findByUuid(UUID id);
@Timed("app.office.partners.repo.findAll.rbac")
List<HsOfficePartnerRbacEntity> findAll(); // TODO.refa: move to a repo in test sources
@Query(value = """
select partner.uuid, partner.detailsuuid, partner.partnernumber, partner.partnerreluuid, partner.version
from hs_office.partner_rv partner
join hs_office.relation partnerRel on partnerRel.uuid = partner.partnerreluuid
join hs_office.contact contact on contact.uuid = partnerRel.contactuuid
join hs_office.person partnerPerson on partnerPerson.uuid = partnerRel.holderuuid
left join hs_office.partner_details_rv partnerDetails on partnerDetails.uuid = partner.detailsuuid
where :name is null
or (partnerDetails.uuid is not null and partnerDetails.birthname like (cast(:name as text) || '%') escape '')
or contact.caption like (cast(:name as text) || '%') escape ''
or partnerPerson.tradename like (cast(:name as text) || '%') escape ''
or partnerPerson.givenname like (cast(:name as text) || '%') escape ''
or partnerPerson.familyname like (cast(:name as text) || '%') escape ''
""", nativeQuery = true)
@Timed("app.office.partners.repo.findPartnerByOptionalNameLike.rbac")
List<HsOfficePartnerRbacEntity> findPartnerByOptionalNameLike(String name);
@Timed("app.office.partners.repo.findPartnerByPartnerNumber.rbac")
Optional<HsOfficePartnerRbacEntity> findPartnerByPartnerNumber(Integer partnerNumber);
@Timed("app.office.partners.repo.save.rbac")
HsOfficePartnerRbacEntity save(final HsOfficePartnerRbacEntity entity);
@Timed("app.office.partners.repo.count.rbac")
long count();
@Timed("app.office.partners.repo.deleteByUuid.rbac")
int deleteByUuid(UUID uuid);
}

View File

@ -0,0 +1,21 @@
package net.hostsharing.hsadminng.hs.office.partner;
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(schema = "hs_office", name = "partner")
@Getter
@Setter
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@DisplayAs("RealPartner")
public class HsOfficePartnerRealEntity extends HsOfficePartner<HsOfficePartnerRealEntity> {
}

View File

@ -0,0 +1,41 @@
package net.hostsharing.hsadminng.hs.office.partner;
import io.micrometer.core.annotation.Timed;
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 HsOfficePartnerRealRepository extends Repository<HsOfficePartnerRealEntity, UUID> {
@Timed("app.office.partners.repo.findByUuid.real")
Optional<HsOfficePartnerRealEntity> findByUuid(UUID id);
@Timed("app.office.partners.repo.findAll.real")
List<HsOfficePartnerRbacEntity> findAll(); // TODO.refa: move to a repo in test sources
@Query(value = """
select partner.uuid, partner.detailsuuid, partner.partnernumber, partner.partnerreluuid, partner.version
from hs_office.partner partner
join hs_office.relation partnerRel on partnerRel.uuid = partner.partnerreluuid
join hs_office.contact contact on contact.uuid = partnerRel.contactuuid
join hs_office.person partnerPerson on partnerPerson.uuid = partnerRel.holderuuid
left join hs_office.partner_details_rv partnerDetails on partnerDetails.uuid = partner.detailsuuid
where :name is null
or (partnerDetails.uuid is not null and partnerDetails.birthname like (cast(:name as text) || '%') escape '')
or contact.caption like (cast(:name as text) || '%') escape ''
or partnerPerson.tradename like (cast(:name as text) || '%') escape ''
or partnerPerson.givenname like (cast(:name as text) || '%') escape ''
or partnerPerson.familyname like (cast(:name as text) || '%') escape ''
""", nativeQuery = true)
@Timed("app.office.partners.repo.findPartnerByOptionalNameLike.real")
List<HsOfficePartnerRealEntity> findPartnerByOptionalNameLike(String name);
@Timed("app.office.partners.repo.findPartnerByPartnerNumber.real")
Optional<HsOfficePartnerRealEntity> findPartnerByPartnerNumber(Integer partnerNumber);
@Timed("app.office.partners.repo.save.real")
HsOfficePartnerRealEntity save(final HsOfficePartnerRealEntity entity);
}

View File

@ -1,45 +0,0 @@
package net.hostsharing.hsadminng.hs.office.partner;
import io.micrometer.core.annotation.Timed;
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 HsOfficePartnerRepository extends Repository<HsOfficePartnerEntity, UUID> {
@Timed("app.office.partners.repo.findByUuid")
Optional<HsOfficePartnerEntity> findByUuid(UUID id);
@Timed("app.office.partners.repo.findAll")
List<HsOfficePartnerEntity> findAll(); // TODO.refa: move to a repo in test sources
@Query("""
SELECT partner FROM HsOfficePartnerEntity partner
JOIN HsOfficeRelationRealEntity rel ON rel.uuid = partner.partnerRel.uuid
JOIN HsOfficeContactRealEntity contact ON contact.uuid = rel.contact.uuid
JOIN HsOfficePersonRealEntity person ON person.uuid = rel.holder.uuid
WHERE :name is null
OR partner.details.birthName like concat(cast(:name as text), '%')
OR contact.caption like concat(cast(:name as text), '%')
OR person.tradeName like concat(cast(:name as text), '%')
OR person.givenName like concat(cast(:name as text), '%')
OR person.familyName like concat(cast(:name as text), '%')
""")
@Timed("app.office.partners.repo.findPartnerByOptionalNameLike")
List<HsOfficePartnerEntity> findPartnerByOptionalNameLike(String name);
@Timed("app.office.partners.repo.findPartnerByPartnerNumber")
Optional<HsOfficePartnerEntity> findPartnerByPartnerNumber(Integer partnerNumber);
@Timed("app.office.partners.repo.save")
HsOfficePartnerEntity save(final HsOfficePartnerEntity entity);
@Timed("app.office.partners.repo.count")
long count();
@Timed("app.office.partners.repo.deleteByUuid")
int deleteByUuid(UUID uuid);
}

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.person; package net.hostsharing.hsadminng.hs.office.person;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonInsertResource;
@ -24,7 +24,7 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficePersonRbacRepository personRepo; private HsOfficePersonRbacRepository personRepo;

View File

@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.office.person;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
@ -17,7 +16,6 @@ import jakarta.persistence.Table;
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@SuperBuilder(toBuilder = true) @SuperBuilder(toBuilder = true)
@FieldNameConstants
@DisplayAs("RealPerson") @DisplayAs("RealPerson")
public class HsOfficePersonRealEntity extends HsOfficePerson<HsOfficePersonRealEntity> { public class HsOfficePersonRealEntity extends HsOfficePerson<HsOfficePersonRealEntity> {
} }

View File

@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelation
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.HsOfficePersonRealEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -32,7 +32,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private HsOfficeRelationRbacRepository rbacRelationRepo; private HsOfficeRelationRbacRepository rbacRelationRepo;

View File

@ -2,11 +2,14 @@ package net.hostsharing.hsadminng.hs.office.sepamandate;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeSepaMandatesApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeSepaMandatesApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateResource;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -15,6 +18,7 @@ import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBui
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import jakarta.validation.ValidationException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -29,7 +33,13 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired
private HsOfficeDebitorRepository debitorRepo;
@Autowired
private HsOfficeBankAccountRepository bankAccountRepo;
@Autowired @Autowired
private HsOfficeSepaMandateRepository sepaMandateRepo; private HsOfficeSepaMandateRepository sepaMandateRepo;
@ -137,10 +147,22 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
if (entity.getValidity().hasUpperBound()) { if (entity.getValidity().hasUpperBound()) {
resource.setValidTo(entity.getValidity().upper().minusDays(1)); resource.setValidTo(entity.getValidity().upper().minusDays(1));
} }
resource.setDebitor(mapper.map(entity.getDebitor(), HsOfficeDebitorResource.class));
resource.getDebitor().setDebitorNumber(entity.getDebitor().getTaggedDebitorNumber()); resource.getDebitor().setDebitorNumber(entity.getDebitor().getTaggedDebitorNumber());
resource.getDebitor().getPartner().setPartnerNumber(entity.getDebitor().getPartner().getTaggedPartnerNumber());
}; };
final BiConsumer<HsOfficeSepaMandateInsertResource, HsOfficeSepaMandateEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { final BiConsumer<HsOfficeSepaMandateInsertResource, HsOfficeSepaMandateEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
entity.setValidity(toPostgresDateRange(resource.getValidFrom(), resource.getValidTo())); entity.setValidity(toPostgresDateRange(resource.getValidFrom(), resource.getValidTo()));
entity.setDebitor(debitorRepo.findByUuid(resource.getDebitorUuid()).orElseThrow( () ->
new ValidationException(
"debitor.uuid='" + resource.getDebitorUuid() + "' not found or not accessible"
)
));
entity.setBankAccount(bankAccountRepo.findByUuid(resource.getBankAccountUuid()).orElseThrow( () ->
new ValidationException(
"bankAccount.uuid='" + resource.getBankAccountUuid() + "' not found or not accessible"
)
));
}; };
} }

View File

@ -0,0 +1,17 @@
package net.hostsharing.hsadminng.hs.validation;
import lombok.experimental.UtilityClass;
import jakarta.validation.ValidationException;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
@UtilityClass
public class UuidResolver {
public static <T> T resolve(final String jsonPath, final UUID uuid, final Function<UUID, Optional<T>> findByUuid) {
return findByUuid.apply(uuid)
.orElseThrow(() -> new ValidationException("Unable to find " + jsonPath + ": " + uuid));
}
}

View File

@ -1,17 +0,0 @@
package net.hostsharing.hsadminng.mapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* A nicer API for ModelMapper in standard mode.
*/
@Component
public class StandardMapper extends Mapper {
public StandardMapper(@Autowired final EntityManagerWrapper em) {
super(em);
getConfiguration().setAmbiguityIgnored(true);
}
}

View File

@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.persistence;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import jakarta.persistence.EntityManager;
import java.util.UUID; import java.util.UUID;
public interface BaseEntity<T extends BaseEntity<?>> { public interface BaseEntity<T extends BaseEntity<?>> {
@ -15,4 +16,10 @@ public interface BaseEntity<T extends BaseEntity<?>> {
//noinspection unchecked //noinspection unchecked
return (T) this; return (T) this;
}; };
default T reload(final EntityManager em) {
em.flush();
em.refresh(this);
return load();
}
} }

View File

@ -1,38 +0,0 @@
package net.hostsharing.hsadminng.persistence;
import net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.persistence.Entity;
import jakarta.validation.ValidationException;
@Service
public class EntityExistsValidator {
@Autowired
private EntityManagerWrapper em;
public <T extends BaseEntity<T>> void validateEntityExists(final String property, final T entitySkeleton) {
final var foundEntity = em.find(entityClass(entitySkeleton), entitySkeleton.getUuid());
if ( foundEntity == null) {
throw new ValidationException("Unable to find " + DisplayName.of(entitySkeleton) + " by " + property + ": " + entitySkeleton.getUuid());
}
}
private static <T extends BaseEntity<T>> Class<?> entityClass(final T entityOrProxy) {
final var entityClass = entityClass(entityOrProxy.getClass());
if (entityClass == null) {
throw new IllegalArgumentException("@Entity not found in superclass hierarchy of " + entityOrProxy.getClass());
}
return entityClass;
}
private static Class<?> entityClass(final Class<?> entityOrProxyClass) {
return entityOrProxyClass.isAnnotationPresent(Entity.class)
? entityOrProxyClass
: entityOrProxyClass.getSuperclass() == null
? null
: entityClass(entityOrProxyClass.getSuperclass());
}
}

View File

@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.rbac.grant;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacGrantResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacGrantResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -23,7 +23,7 @@ public class RbacGrantController implements RbacGrantsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private RbacGrantRepository rbacGrantRepository; private RbacGrantRepository rbacGrantRepository;

View File

@ -49,8 +49,18 @@ public class RbacGrantsDiagramService {
NON_TEST_ENTITIES; NON_TEST_ENTITIES;
public static final EnumSet<Include> ALL = EnumSet.allOf(Include.class); public static final EnumSet<Include> ALL = EnumSet.allOf(Include.class);
public static final EnumSet<Include> ALL_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, TEST_ENTITIES, PERMISSIONS); public static final EnumSet<Include> ALL_TEST_ENTITY_RELATED = EnumSet.of(
public static final EnumSet<Include> ALL_NON_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, NON_TEST_ENTITIES, PERMISSIONS); USERS,
DETAILS,
NOT_ASSUMED,
TEST_ENTITIES,
PERMISSIONS);
public static final EnumSet<Include> ALL_NON_TEST_ENTITY_RELATED = EnumSet.of(
USERS,
DETAILS,
NOT_ASSUMED,
NON_TEST_ENTITIES,
PERMISSIONS);
} }
@Autowired @Autowired
@ -94,12 +104,17 @@ public class RbacGrantsDiagramService {
} }
public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> includes) { public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> includes) {
final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbac.permission WHERE objectuuid=:targetObject AND op=:op") final var graph = new LimitedHashSet<RawRbacGrantEntity>();
@SuppressWarnings("unchecked") // List -> List<List<UUID>>
final var refUuidLists = (List<List<UUID>>) em.createNativeQuery(
"select uuid from rbac.permission where objectUuid=:targetObject and op=:op",
List.class)
.setParameter("targetObject", targetObject) .setParameter("targetObject", targetObject)
.setParameter("op", op) .setParameter("op", op)
.getSingleResult(); .getResultList();
final var graph = new LimitedHashSet<RawRbacGrantEntity>(); refUuidLists.stream().flatMap(Collection::stream)
traverseGrantsFrom(graph, refUuid, includes); .forEach(refUuid -> traverseGrantsFrom(graph, refUuid, includes));
return toMermaidFlowchart(graph, includes); return toMermaidFlowchart(graph, includes);
} }
@ -215,10 +230,15 @@ public class RbacGrantsDiagramService {
@NotNull @NotNull
private static String cleanId(final String idName) { private static String cleanId(final String idName) {
return idName.replaceAll("@.*", "") return idName.replaceAll("@.*", "")
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "").replace(">", ":").replace("|", "_"); .replace("[", "")
.replace("]", "")
.replace("(", "")
.replace(")", "")
.replace(",", "")
.replace(">", ":")
.replace("|", "_");
} }
static class LimitedHashSet<T> extends HashSet<T> { static class LimitedHashSet<T> extends HashSet<T> {
@Override @Override

View File

@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.rbac.role;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacRoleResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacRoleResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -19,7 +19,7 @@ public class RbacRoleController implements RbacRolesApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private RbacRoleRepository rbacRoleRepository; private RbacRoleRepository rbacRoleRepository;

View File

@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.rbac.subject;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource;
@ -22,7 +22,7 @@ public class RbacSubjectController implements RbacSubjectsApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private RbacSubjectRepository rbacSubjectRepository; private RbacSubjectRepository rbacSubjectRepository;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.rbac.test.cust; package net.hostsharing.hsadminng.rbac.test.cust;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi; import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi;
import net.hostsharing.hsadminng.test.generated.api.v1.model.TestCustomerResource; import net.hostsharing.hsadminng.test.generated.api.v1.model.TestCustomerResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -21,7 +21,7 @@ public class TestCustomerController implements TestCustomersApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private TestCustomerRepository testCustomerRepository; private TestCustomerRepository testCustomerRepository;

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.rbac.test.pac; package net.hostsharing.hsadminng.rbac.test.pac;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.mapper.OptionalFromJson; import net.hostsharing.hsadminng.mapper.OptionalFromJson;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestPackagesApi; import net.hostsharing.hsadminng.test.generated.api.v1.api.TestPackagesApi;
@ -21,7 +21,7 @@ public class TestPackageController implements TestPackagesApi {
private Context context; private Context context;
@Autowired @Autowired
private StandardMapper mapper; private StrictMapper mapper;
@Autowired @Autowired
private TestPackageRepository testPackageRepository; private TestPackageRepository testPackageRepository;

View File

@ -90,6 +90,7 @@ components:
type: boolean type: boolean
vatReverseCharge: vatReverseCharge:
type: boolean type: boolean
# TODO.feat: alternatively the complete refundBankAccount
refundBankAccount.uuid: refundBankAccount.uuid:
type: string type: string
format: uuid format: uuid

View File

@ -43,7 +43,10 @@ end; $$;
do language plpgsql $$ do language plpgsql $$
begin begin
call base.defineContext('creating coopSharesTransaction test-data'); call base.defineContext('creating coopSharesTransaction test-data',
null,
'superuser-alex@hostsharing.net',
'rbac.global#global:ADMIN');
SET CONSTRAINTS ALL DEFERRED; SET CONSTRAINTS ALL DEFERRED;
call hs_office.coopsharetx_create_test_data(10001, '01'); call hs_office.coopsharetx_create_test_data(10001, '01');

View File

@ -49,7 +49,10 @@ end; $$;
do language plpgsql $$ do language plpgsql $$
begin begin
call base.defineContext('creating coopAssetsTransaction test-data'); call base.defineContext('creating coopAssetsTransaction test-data',
null,
'superuser-alex@hostsharing.net',
'rbac.global#global:ADMIN');
SET CONSTRAINTS ALL DEFERRED; SET CONSTRAINTS ALL DEFERRED;
call hs_office.coopassettx_create_test_data(10001, '01'); call hs_office.coopassettx_create_test_data(10001, '01');

View File

@ -81,8 +81,7 @@ class RestResponseEntityExceptionHandlerUnitTest {
void handleJpaObjectRetrievalFailureExceptionWithDisplayName() { void handleJpaObjectRetrievalFailureExceptionWithDisplayName() {
// given // given
final var givenException = new JpaObjectRetrievalFailureException( final var givenException = new JpaObjectRetrievalFailureException(
new EntityNotFoundException( new EntityNotFoundException("Unable to find Entity with id 12345-123454")
"Unable to find net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity with id 12345-123454")
); );
final var givenWebRequest = mock(WebRequest.class); final var givenWebRequest = mock(WebRequest.class);
@ -91,7 +90,7 @@ class RestResponseEntityExceptionHandlerUnitTest {
// then // then
assertThat(errorResponse.getBody().getStatusCode()).isEqualTo(400); assertThat(errorResponse.getBody().getStatusCode()).isEqualTo(400);
assertThat(errorResponse.getBody().getMessage()).isEqualTo("ERROR: [400] Unable to find Partner with uuid 12345-123454"); assertThat(errorResponse.getBody().getMessage()).isEqualTo("ERROR: [400] Unable to find Entity with id 12345-123454");
} }
@Test @Test

View File

@ -643,7 +643,6 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
final var givenProject = realProjectRepo.findByCaption(projectCaption).stream() final var givenProject = realProjectRepo.findByCaption(projectCaption).stream()
.findAny().orElseThrow(); .findAny().orElseThrow();
final var newBookingItem = HsBookingItemRealEntity.builder() final var newBookingItem = HsBookingItemRealEntity.builder()
.uuid(UUID.randomUUID())
.project(givenProject) .project(givenProject)
.type(hsBookingItemType) .type(hsBookingItemType)
.caption("some test-booking") .caption("some test-booking")

View File

@ -14,7 +14,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -48,23 +48,23 @@ class HsBookingItemControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@Autowired @Autowired
@SuppressWarnings("unused") // not used in test, but in controller class @SuppressWarnings("unused") // not used in test, but in controller class
StrictMapper mapper; StrictMapper mapper;
@MockBean @MockitoBean
EntityManagerWrapper em; EntityManagerWrapper em;
@MockBean @MockitoBean
EntityManagerFactory emf; EntityManagerFactory emf;
@MockBean @MockitoBean
HsBookingProjectRealRepository realProjectRepo; HsBookingProjectRealRepository realProjectRepo;
@MockBean @MockitoBean
HsBookingItemRbacRepository rbacBookingItemRepo; HsBookingItemRbacRepository rbacBookingItemRepo;
@TestConfiguration @TestConfiguration

View File

@ -13,7 +13,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
@ -61,7 +61,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Test @Test

View File

@ -270,7 +270,6 @@ class HsBookingProjectControllerAcceptanceTest extends ContextBasedTestWithClean
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenDebitor = debitorRepo.findByDebitorNumber(debitorNumber).stream().findAny().orElseThrow(); final var givenDebitor = debitorRepo.findByDebitorNumber(debitorNumber).stream().findAny().orElseThrow();
final var newBookingProject = HsBookingProjectRealEntity.builder() final var newBookingProject = HsBookingProjectRealEntity.builder()
.uuid(UUID.randomUUID())
.debitor(givenDebitor) .debitor(givenDebitor)
.caption(caption) .caption(caption)
.build(); .build();

View File

@ -13,7 +13,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
@ -56,7 +56,7 @@ class HsBookingProjectRepositoryIntegrationTest extends ContextBasedTestWithClea
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Test @Test

View File

@ -488,7 +488,6 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
final var givenAsset = givenSomeTemporaryHostingAsset(() -> final var givenAsset = givenSomeTemporaryHostingAsset(() ->
HsHostingAssetRealEntity.builder() HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.bookingItem(givenSomeNewBookingItem( .bookingItem(givenSomeNewBookingItem(
"D-1000111 default project", "D-1000111 default project",
HsBookingItemType.MANAGED_SERVER, HsBookingItemType.MANAGED_SERVER,
@ -571,7 +570,6 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
final var givenAsset = givenSomeTemporaryHostingAsset(() -> final var givenAsset = givenSomeTemporaryHostingAsset(() ->
HsHostingAssetRealEntity.builder() HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.type(UNIX_USER) .type(UNIX_USER)
.parentAsset(givenRealHostingAsset(MANAGED_WEBSPACE, "fir01")) .parentAsset(givenRealHostingAsset(MANAGED_WEBSPACE, "fir01"))
.identifier("fir01-temp") .identifier("fir01-temp")
@ -648,7 +646,6 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAsset = givenSomeTemporaryHostingAsset(() -> final var givenAsset = givenSomeTemporaryHostingAsset(() ->
HsHostingAssetRealEntity.builder() HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.bookingItem(givenSomeNewBookingItem( .bookingItem(givenSomeNewBookingItem(
"D-1000111 default project", "D-1000111 default project",
HsBookingItemType.MANAGED_SERVER, HsBookingItemType.MANAGED_SERVER,
@ -681,7 +678,6 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenAsset = givenSomeTemporaryHostingAsset(() -> final var givenAsset = givenSomeTemporaryHostingAsset(() ->
HsHostingAssetRealEntity.builder() HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.bookingItem(givenSomeNewBookingItem( .bookingItem(givenSomeNewBookingItem(
"D-1000111 default project", "D-1000111 default project",
HsBookingItemType.MANAGED_SERVER, HsBookingItemType.MANAGED_SERVER,

View File

@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.mapper.Array;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.config.DisableSecurityConfig;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -24,7 +24,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@ -54,7 +54,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(HsHostingAssetController.class) @WebMvcTest(HsHostingAssetController.class)
@Import({ StandardMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class }) @Import({ StrictMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class })
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ActiveProfiles("test") @ActiveProfiles("test")
public class HsHostingAssetControllerRestTest { public class HsHostingAssetControllerRestTest {
@ -62,27 +62,27 @@ public class HsHostingAssetControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@Autowired @Autowired
@SuppressWarnings("unused") // not used in test, but in controller class @SuppressWarnings("unused") // not used in test, but in controller class
StandardMapper mapper; StrictMapper mapper;
@MockBean @MockitoBean
EntityManagerWrapper em; EntityManagerWrapper em;
@MockBean @MockitoBean
EntityManagerFactory emf; EntityManagerFactory emf;
@MockBean @MockitoBean
@SuppressWarnings("unused") // bean needs to be present for HsHostingAssetController @SuppressWarnings("unused") // bean needs to be present for HsHostingAssetController
private HsBookingItemRealRepository realBookingItemRepo; private HsBookingItemRealRepository realBookingItemRepo;
@MockBean @MockitoBean
private HsHostingAssetRealRepository realAssetRepo; private HsHostingAssetRealRepository realAssetRepo;
@MockBean @MockitoBean
private HsHostingAssetRbacRepository rbacAssetRepo; private HsHostingAssetRbacRepository rbacAssetRepo;
@TestConfiguration @TestConfiguration

View File

@ -17,7 +17,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
@ -70,7 +70,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Test @Test

View File

@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration; import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@ -59,7 +59,7 @@ class DomainSetupHostingAssetFactoryUnitTest {
private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build(); private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build();
@Spy @Spy
private StandardMapper standardMapper = new StandardMapper(emw); private StrictMapper StrictMapper = new StrictMapper(emw);
@InjectMocks @InjectMocks
private HsBookingItemCreatedListener listener; private HsBookingItemCreatedListener listener;

View File

@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedEventEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.lambda.Reducer; import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake; import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -42,7 +42,7 @@ class HsBookingItemCreatedListenerUnitTest {
private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build(); private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build();
@Spy @Spy
private StandardMapper standardMapper = new StandardMapper(emw); private StrictMapper StrictMapper = new StrictMapper(emw);
@InjectMocks @InjectMocks
private HsBookingItemCreatedListener listener; private HsBookingItemCreatedListener listener;

View File

@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.lambda.Reducer; import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake; import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -55,7 +55,7 @@ class ManagedWebspaceHostingAssetFactoryUnitTest {
private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build(); private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build();
@Spy @Spy
private StandardMapper standardMapper = new StandardMapper(emw); private StrictMapper StrictMapper = new StrictMapper(emw);
@InjectMocks @InjectMocks
private HsBookingItemCreatedListener listener; private HsBookingItemCreatedListener listener;

View File

@ -10,7 +10,7 @@ import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus;
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.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
@ -84,7 +84,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
static Map<Integer, HsOfficeContactRealEntity> contacts = new WriteOnceMap<>(); static Map<Integer, HsOfficeContactRealEntity> contacts = new WriteOnceMap<>();
static Map<Integer, HsOfficePersonRealEntity> persons = new WriteOnceMap<>(); static Map<Integer, HsOfficePersonRealEntity> persons = new WriteOnceMap<>();
static Map<Integer, HsOfficePartnerEntity> partners = new WriteOnceMap<>(); static Map<Integer, HsOfficePartnerRealEntity> partners = new WriteOnceMap<>();
static Map<Integer, HsOfficeDebitorEntity> debitors = new WriteOnceMap<>(); static Map<Integer, HsOfficeDebitorEntity> debitors = new WriteOnceMap<>();
static Map<Integer, HsOfficeMembershipEntity> memberships = new WriteOnceMap<>(); static Map<Integer, HsOfficeMembershipEntity> memberships = new WriteOnceMap<>();
@ -743,7 +743,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
null // is set during contacts import depending on assigned roles null // is set during contacts import depending on assigned roles
); );
final var partner = HsOfficePartnerEntity.builder() final var partner = HsOfficePartnerRealEntity.builder()
.partnerNumber(rec.getInteger("member_id")) .partnerNumber(rec.getInteger("member_id"))
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
.partnerRel(partnerRel) .partnerRel(partnerRel)

View File

@ -14,7 +14,7 @@ import org.junit.jupiter.api.extension.TestWatcher;
import org.opentest4j.AssertionFailedError; import org.opentest4j.AssertionFailedError;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
@ -76,7 +76,7 @@ public class CsvDataImport extends ContextBasedTest {
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
static final LinkedHashSet<String> errors = new LinkedHashSet<>(); static final LinkedHashSet<String> errors = new LinkedHashSet<>();

View File

@ -1,13 +1,13 @@
package net.hostsharing.hsadminng.hs.office.bankaccount; package net.hostsharing.hsadminng.hs.office.bankaccount;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.config.DisableSecurityConfig;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
@ -26,14 +26,14 @@ class HsOfficeBankAccountControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@MockBean @MockitoBean
@SuppressWarnings("unused") // not used in test, but in controller class @SuppressWarnings("unused") // not used in test, but in controller class
StandardMapper mapper; StrictMapper mapper;
@MockBean @MockitoBean
HsOfficeBankAccountRepository bankAccountRepo; HsOfficeBankAccountRepository bankAccountRepo;
enum InvalidIbanTestCase { enum InvalidIbanTestCase {

View File

@ -11,7 +11,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -46,7 +46,7 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -374,7 +374,6 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.define(creatingUser); context.define(creatingUser);
final var newContact = HsOfficeContactRbacEntity.builder() final var newContact = HsOfficeContactRbacEntity.builder()
.uuid(UUID.randomUUID())
.caption("Temp from " + Context.getCallerMethodNameFromStackFrame(1) ) .caption("Temp from " + Context.getCallerMethodNameFromStackFrame(1) )
.postalAddress(Map.ofEntries( .postalAddress(Map.ofEntries(
entry("name", RandomStringUtils.randomAlphabetic(6) + " " + RandomStringUtils.randomAlphabetic(10)), entry("name", RandomStringUtils.randomAlphabetic(6) + " " + RandomStringUtils.randomAlphabetic(10)),

View File

@ -11,7 +11,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -46,7 +46,7 @@ class HsOfficeContactRbacRepositoryIntegrationTest extends ContextBasedTestWithC
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.rbac.test.JsonBuilder; import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
@ -17,10 +17,10 @@ import org.junit.jupiter.params.provider.EnumSource;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@ -67,7 +67,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
private static final String ORIGIN_MEMBER_NUMBER = "M-1111100"; private static final String ORIGIN_MEMBER_NUMBER = "M-1111100";
public final HsOfficeMembershipEntity ORIGIN_TARGET_MEMBER_ENTITY = HsOfficeMembershipEntity.builder() public final HsOfficeMembershipEntity ORIGIN_TARGET_MEMBER_ENTITY = HsOfficeMembershipEntity.builder()
.uuid(ORIGIN_MEMBERSHIP_UUID) .uuid(ORIGIN_MEMBERSHIP_UUID)
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerRealEntity.builder()
.partnerNumber(partnerNumberOf(ORIGIN_MEMBER_NUMBER)) .partnerNumber(partnerNumberOf(ORIGIN_MEMBER_NUMBER))
.build()) .build())
.memberNumberSuffix(suffixOf(ORIGIN_MEMBER_NUMBER)) .memberNumberSuffix(suffixOf(ORIGIN_MEMBER_NUMBER))
@ -77,7 +77,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
private static final String AVAILABLE_TARGET_MEMBER_NUMBER = "M-1234500"; private static final String AVAILABLE_TARGET_MEMBER_NUMBER = "M-1234500";
public final HsOfficeMembershipEntity AVAILABLE_MEMBER_ENTITY = HsOfficeMembershipEntity.builder() public final HsOfficeMembershipEntity AVAILABLE_MEMBER_ENTITY = HsOfficeMembershipEntity.builder()
.uuid(AVAILABLE_TARGET_MEMBERSHIP_UUID) .uuid(AVAILABLE_TARGET_MEMBERSHIP_UUID)
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerRealEntity.builder()
.partnerNumber(partnerNumberOf(AVAILABLE_TARGET_MEMBER_NUMBER)) .partnerNumber(partnerNumberOf(AVAILABLE_TARGET_MEMBER_NUMBER))
.build()) .build())
.memberNumberSuffix(suffixOf(AVAILABLE_TARGET_MEMBER_NUMBER)) .memberNumberSuffix(suffixOf(AVAILABLE_TARGET_MEMBER_NUMBER))
@ -499,20 +499,20 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@Autowired @Autowired
@SuppressWarnings("unused") // not used in test, but in controller class @SuppressWarnings("unused") // not used in test, but in controller class
StrictMapper mapper; StrictMapper mapper;
@MockBean @MockitoBean
EntityManagerWrapper emw; // even if not used in test anymore, it's needed by base-class of StrictMapper EntityManagerWrapper emw; // even if not used in test anymore, it's needed by base-class of StrictMapper
@MockBean @MockitoBean
HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo; HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
@MockBean @MockitoBean
HsOfficeMembershipRepository membershipRepo; HsOfficeMembershipRepository membershipRepo;
static final String INSERT_REQUEST_BODY_TEMPLATE = """ static final String INSERT_REQUEST_BODY_TEMPLATE = """

View File

@ -13,7 +13,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -51,7 +51,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -170,7 +170,9 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow();
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given().header("current-subject", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" .given()
.header("current-subject", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON).body("""
{ {
"membership.uuid": "%s", "membership.uuid": "%s",
"transactionType": "SUBSCRIPTION", "transactionType": "SUBSCRIPTION",
@ -179,15 +181,29 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
"reference": "temp ref A", "reference": "temp ref A",
"comment": "just some test coop shares transaction" "comment": "just some test coop shares transaction"
} }
""".formatted(givenMembership.getUuid())).port(port).when().post("http://localhost/api/hs/office/coopsharestransactions").then().log().all().assertThat().statusCode(201).contentType(ContentType.JSON).body("uuid", isUuidValid()).body("", lenientlyEquals(""" """.formatted(givenMembership.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/coopsharestransactions")
.then()
.log().all()
.assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("", lenientlyEquals("""
{ {
"membership.uuid": "%s",
"transactionType": "SUBSCRIPTION", "transactionType": "SUBSCRIPTION",
"shareCount": 8, "shareCount": 8,
"valueDate": "2022-10-13", "valueDate": "2022-10-13",
"reference": "temp ref A", "reference": "temp ref A",
"comment": "just some test coop shares transaction" "comment": "just some test coop shares transaction"
} }
""")).header("Location", startsWith("http://localhost")).extract().header("Location"); // @formatter:on """.formatted(givenMembership.getUuid())))
.header("Location", startsWith("http://localhost"))
.extract()
.header("Location"); // @formatter:on
// finally, the new coopSharesTransaction can be accessed under the generated UUID // finally, the new coopSharesTransaction can be accessed under the generated UUID
final var newShareTxUuid = UUID.fromString(location.substring(location.lastIndexOf('/') + 1)); final var newShareTxUuid = UUID.fromString(location.substring(location.lastIndexOf('/') + 1));
@ -197,7 +213,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
@Test @Test
void globalAdmin_canAddCoopSharesReversalTransaction() { void globalAdmin_canAddCoopSharesReversalTransaction() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net", "rbac.global#global:ADMIN");
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow();
final var givenTransaction = jpaAttempt.transacted(() -> { final var givenTransaction = jpaAttempt.transacted(() -> {
// TODO.impl: introduce something like transactedAsSuperuser / transactedAs("...", ...) // TODO.impl: introduce something like transactedAsSuperuser / transactedAs("...", ...)
@ -269,7 +285,10 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow(); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101).orElseThrow();
RestAssured // @formatter:off RestAssured // @formatter:off
.given().header("current-subject", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" .given()
.header("current-subject", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{ {
"membership.uuid": "%s", "membership.uuid": "%s",
"transactionType": "CANCELLATION", "transactionType": "CANCELLATION",
@ -278,7 +297,16 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
"reference": "temp ref X", "reference": "temp ref X",
"comment": "just some test coop shares transaction" "comment": "just some test coop shares transaction"
} }
""".formatted(givenMembership.getUuid())).port(port).when().post("http://localhost/api/hs/office/coopsharestransactions").then().log().all().assertThat().statusCode(400).contentType(ContentType.JSON).body("", lenientlyEquals(""" """.formatted(givenMembership.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/coopsharestransactions")
.then()
.log().all()
.assertThat()
.statusCode(400)
.contentType(ContentType.JSON)
.body("", lenientlyEquals("""
{ {
"statusCode": 400, "statusCode": 400,
"statusPhrase": "Bad Request", "statusPhrase": "Bad Request",

View File

@ -1,14 +1,15 @@
package net.hostsharing.hsadminng.hs.office.coopshares; package net.hostsharing.hsadminng.hs.office.coopshares;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.rbac.test.JsonBuilder; import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.config.DisableSecurityConfig;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
@ -31,16 +32,19 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@MockBean @MockitoBean
@SuppressWarnings("unused") // not used in test, but in controller class @SuppressWarnings("unused") // not used in test, but in controller class
StandardMapper mapper; StrictMapper mapper;
@MockBean @MockitoBean
HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo; HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
@MockitoBean
HsOfficeMembershipRepository membershipRepo;
static final String VALID_INSERT_REQUEST_BODY = """ static final String VALID_INSERT_REQUEST_BODY = """
{ {
"membership.uuid": "%s", "membership.uuid": "%s",

View File

@ -13,7 +13,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -50,7 +50,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRbacRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
@ -28,6 +28,7 @@ import jakarta.persistence.PersistenceContext;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.rbac.role.RbacRoleType.ADMIN;
import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -57,7 +58,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
HsOfficeDebitorRepository debitorRepo; HsOfficeDebitorRepository debitorRepo;
@Autowired @Autowired
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRbacRepository partnerRepo;
@Autowired @Autowired
HsOfficeContactRealRepository contactRealRepo; HsOfficeContactRealRepository contactRealRepo;
@ -467,7 +468,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.post("http://localhost/api/hs/office/debitors") .post("http://localhost/api/hs/office/debitors")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(400) .statusCode(400)
.body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contactUuid: 00000000-0000-0000-0000-000000000000")); .body("message", is("ERROR: [400] Unable to find debitorRel.contact.uuid: 00000000-0000-0000-0000-000000000000"));
// @formatter:on // @formatter:on
} }
@ -495,7 +496,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.post("http://localhost/api/hs/office/debitors") .post("http://localhost/api/hs/office/debitors")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(400) .statusCode(400)
.body("message", is("ERROR: [400] Unable to find RealRelation by debitorRelUuid: 00000000-0000-0000-0000-000000000000")); .body("message", is("ERROR: [400] Unable to find debitorRel.uuid: 00000000-0000-0000-0000-000000000000"));
// @formatter:on // @formatter:on
} }
} }
@ -695,7 +696,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
} }
@Test @Test
void theContactOwner_canNotPatchARelatedDebitor() { void theContactAdmin_canNotPatchARelatedDebitor() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor(); final var givenDebitor = givenSomeTemporaryDebitor();
@ -704,7 +705,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.header("assumed-roles", "hs_office.contact#tenthcontact:ADMIN") .header("assumed-roles", givenDebitor.getDebitorRel().getContact().roleId(ADMIN) )
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
@ -802,7 +803,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.vatReverseCharge(false) .vatReverseCharge(false)
.build(); .build();
return debitorRepo.save(newDebitor).load(); return debitorRepo.save(newDebitor).reload(em);
}).assertSuccessful().returnedValue(); }).assertSuccessful().returnedValue();
} }

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.debitor; package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
@ -30,7 +30,7 @@ class HsOfficeDebitorEntityUnitTest {
.debitorNumberSuffix("67") .debitorNumberSuffix("67")
.debitorRel(givenDebitorRel) .debitorRel(givenDebitorRel)
.defaultPrefix("som") .defaultPrefix("som")
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerRealEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.build()) .build())
.build(); .build();
@ -45,7 +45,7 @@ class HsOfficeDebitorEntityUnitTest {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel) .debitorRel(givenDebitorRel)
.debitorNumberSuffix("67") .debitorNumberSuffix("67")
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerRealEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.build()) .build())
.build(); .build();
@ -60,7 +60,7 @@ class HsOfficeDebitorEntityUnitTest {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel) .debitorRel(givenDebitorRel)
.debitorNumberSuffix("67") .debitorNumberSuffix("67")
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerRealEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.build()) .build())
.build(); .build();
@ -88,7 +88,7 @@ class HsOfficeDebitorEntityUnitTest {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel) .debitorRel(givenDebitorRel)
.debitorNumberSuffix("67") .debitorNumberSuffix("67")
.partner(HsOfficePartnerEntity.builder().build()) .partner(HsOfficePartnerRealEntity.builder().build())
.build(); .build();
final var result = given.getTaggedDebitorNumber(); final var result = given.getTaggedDebitorNumber();
@ -101,7 +101,7 @@ class HsOfficeDebitorEntityUnitTest {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel) .debitorRel(givenDebitorRel)
.debitorNumberSuffix(null) .debitorNumberSuffix(null)
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerRealEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.build()) .build())
.build(); .build();

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
@ -22,7 +22,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -48,7 +48,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
HsOfficeDebitorRepository debitorRepo; HsOfficeDebitorRepository debitorRepo;
@Autowired @Autowired
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRealRepository partnerRepo;
@Autowired @Autowired
HsOfficeContactRealRepository contactRealRepo; HsOfficeContactRealRepository contactRealRepo;
@ -74,7 +74,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired @Autowired
RbacGrantsDiagramService mermaidService; RbacGrantsDiagramService mermaidService;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested
class CreateDebitor { class CreateDebitor {

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import static net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealTestEntity.TEST_REAL_CONTACT; import static net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealTestEntity.TEST_REAL_CONTACT;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; import static net.hostsharing.hsadminng.hs.office.partner.HsOfficeTestRealPartner.TEST_PARTNER;
@UtilityClass @UtilityClass
public class TestHsOfficeDebitor { public class TestHsOfficeDebitor {

View File

@ -5,7 +5,7 @@ import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
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.partner.HsOfficePartnerRepository; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealRepository;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.config.DisableSecurityConfig;
@ -54,7 +54,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
HsOfficeMembershipRepository membershipRepo; HsOfficeMembershipRepository membershipRepo;
@Autowired @Autowired
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRealRepository partnerRepo;
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@ -430,7 +430,6 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerName).get(0); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerName).get(0);
final var newMembership = HsOfficeMembershipEntity.builder() final var newMembership = HsOfficeMembershipEntity.builder()
.uuid(UUID.randomUUID())
.partner(givenPartner) .partner(givenPartner)
.memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX) .memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX)
.validity(Range.closedInfinite(LocalDate.parse("2022-11-01"))) .validity(Range.closedInfinite(LocalDate.parse("2022-11-01")))

View File

@ -2,8 +2,10 @@ package net.hostsharing.hsadminng.hs.office.membership;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository; import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRbacEntity;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealRepository;
import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.config.DisableSecurityConfig;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
@ -12,7 +14,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
@ -34,11 +36,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(HsOfficeMembershipController.class) @WebMvcTest(HsOfficeMembershipController.class)
@Import({StandardMapper.class, DisableSecurityConfig.class}) @Import({StrictMapper.class, DisableSecurityConfig.class})
@ActiveProfiles("test") @ActiveProfiles("test")
public class HsOfficeMembershipControllerRestTest { public class HsOfficeMembershipControllerRestTest {
private static final HsOfficePartnerEntity PARTNER_12345 = HsOfficePartnerEntity.builder() private static final HsOfficePartnerRealEntity PARTNER_12345 = HsOfficePartnerRealEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.build(); .build();
public static final HsOfficeMembershipEntity MEMBERSHIP_1234501 = HsOfficeMembershipEntity.builder() public static final HsOfficeMembershipEntity MEMBERSHIP_1234501 = HsOfficeMembershipEntity.builder()
@ -69,16 +71,19 @@ public class HsOfficeMembershipControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@MockBean @MockitoBean
HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo; HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
@MockBean @MockitoBean
HsOfficePartnerRealRepository partnerRepo;
@MockitoBean
HsOfficeMembershipRepository membershipRepo; HsOfficeMembershipRepository membershipRepo;
@MockBean @MockitoBean
EntityManagerWrapper em; EntityManagerWrapper em;
@Nested @Nested
@ -252,7 +257,7 @@ public class HsOfficeMembershipControllerRestTest {
// given // given
final var givenPartnerUuid = UUID.randomUUID(); final var givenPartnerUuid = UUID.randomUUID();
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null); when(em.find(HsOfficePartnerRbacEntity.class, givenPartnerUuid)).thenReturn(null);
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
@ -275,7 +280,7 @@ public class HsOfficeMembershipControllerRestTest {
.andExpect(jsonPath("statusPhrase", is("Bad Request"))) .andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath( .andExpect(jsonPath(
"message", "message",
is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid))); is("ERROR: [400] partnerUuid " + givenPartnerUuid + " not found")));
} }
@ParameterizedTest @ParameterizedTest

View File

@ -4,7 +4,7 @@ import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase; import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -17,7 +17,7 @@ import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; import static net.hostsharing.hsadminng.hs.office.partner.HsOfficeTestRealPartner.TEST_PARTNER;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -40,7 +40,7 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
@Mock @Mock
private EntityManagerWrapper em; private EntityManagerWrapper em;
private StandardMapper mapper = new StandardMapper(em); private StrictMapper mapper = new StrictMapper(em);
@BeforeEach @BeforeEach
void initMocks() { void initMocks() {

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.membership; package net.hostsharing.hsadminng.hs.office.membership;
import io.hypersistence.utils.hibernate.type.range.Range; import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacViewMermaidFlowchartGenerator; import net.hostsharing.hsadminng.rbac.generator.RbacViewMermaidFlowchartGenerator;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -10,7 +10,7 @@ import java.lang.reflect.InvocationTargetException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Arrays; import java.util.Arrays;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; import static net.hostsharing.hsadminng.hs.office.partner.HsOfficeTestRealPartner.TEST_PARTNER;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
class HsOfficeMembershipEntityUnitTest { class HsOfficeMembershipEntityUnitTest {
@ -57,7 +57,7 @@ class HsOfficeMembershipEntityUnitTest {
@Test @Test
void getMemberNumberWithoutPartnerNumberButWithSuffix() { void getMemberNumberWithoutPartnerNumberButWithSuffix() {
givenMembership.setPartner(HsOfficePartnerEntity.builder().build()); givenMembership.setPartner(HsOfficePartnerRealEntity.builder().build());
final var result = givenMembership.getMemberNumber(); final var result = givenMembership.getMemberNumber();
assertThat(result).isEqualTo(null); assertThat(result).isEqualTo(null);
} }

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.membership;
import io.hypersistence.utils.hibernate.type.range.Range; import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRealRepository;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.grant.RawRbacGrantRepository; import net.hostsharing.hsadminng.rbac.grant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.role.RawRbacRoleRepository; import net.hostsharing.hsadminng.rbac.role.RawRbacRoleRepository;
@ -13,7 +13,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
@ -37,7 +37,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
HsOfficeMembershipRepository membershipRepo; HsOfficeMembershipRepository membershipRepo;
@Autowired @Autowired
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRealRepository partnerRepo;
@Autowired @Autowired
HsOfficeDebitorRepository debitorRepo; HsOfficeDebitorRepository debitorRepo;
@ -54,7 +54,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -4,7 +4,7 @@ import io.hypersistence.utils.hibernate.type.range.Range;
import java.time.LocalDate; import java.time.LocalDate;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; import static net.hostsharing.hsadminng.hs.office.partner.HsOfficeTestRealPartner.TEST_PARTNER;
public class TestHsMembership { public class TestHsMembership {

View File

@ -42,7 +42,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
private Integer port; private Integer port;
@Autowired @Autowired
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRbacRepository partnerRepo;
@Autowired @Autowired
HsOfficeRelationRealRepository relationRepo; HsOfficeRelationRealRepository relationRepo;
@ -541,12 +541,12 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
return partnerRel; return partnerRel;
}).assertSuccessful().returnedValue(); }).assertSuccessful().returnedValue();
} }
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final Integer partnerNumber) { private HsOfficePartnerRbacEntity givenSomeTemporaryPartnerBessler(final Integer partnerNumber) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var partnerRel = em.merge(givenSomeTemporaryPartnerRel("Erben Bessler", "fourth contact")); final var partnerRel = em.merge(givenSomeTemporaryPartnerRel("Erben Bessler", "fourth contact"));
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerRbacEntity.builder()
.partnerRel(partnerRel) .partnerRel(partnerRel)
.partnerNumber(partnerNumber) .partnerNumber(partnerNumber)
.details(HsOfficePartnerDetailsEntity.builder() .details(HsOfficePartnerDetailsEntity.builder()
@ -561,7 +561,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
@AfterEach @AfterEach
void cleanup() { void cleanup() {
cleanupAllNew(HsOfficePartnerEntity.class); cleanupAllNew(HsOfficePartnerRbacEntity.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(HsOfficeRelationRealEntity.class); cleanupAllNew(HsOfficeRelationRealEntity.class);

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.config.DisableSecurityConfig;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(HsOfficePartnerController.class) @WebMvcTest(HsOfficePartnerController.class)
@Import({StandardMapper.class, DisableSecurityConfig.class}) @Import({StrictMapper.class, DisableSecurityConfig.class})
@ActiveProfiles("test") @ActiveProfiles("test")
class HsOfficePartnerControllerRestTest { class HsOfficePartnerControllerRestTest {
@ -50,19 +50,19 @@ class HsOfficePartnerControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@MockBean @MockitoBean
Context contextMock; Context contextMock;
@MockBean @MockitoBean
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRbacRepository partnerRepo;
@MockBean @MockitoBean
HsOfficeRelationRealRepository relationRepo; HsOfficeRelationRealRepository relationRepo;
@MockBean @MockitoBean
EntityManagerWrapper em; EntityManagerWrapper em;
@MockBean @MockitoBean
EntityManagerFactory emf; EntityManagerFactory emf;
@Mock @Mock
@ -75,7 +75,7 @@ class HsOfficePartnerControllerRestTest {
HsOfficeContactRbacEntity contactMock; HsOfficeContactRbacEntity contactMock;
@Mock @Mock
HsOfficePartnerEntity partnerMock; HsOfficePartnerRbacEntity partnerMock;
@BeforeEach @BeforeEach
void init() { void init() {
@ -174,7 +174,7 @@ class HsOfficePartnerControllerRestTest {
@Test @Test
void respondWithPartner_ifPartnerNumberIsAvailable() throws Exception { void respondWithPartner_ifPartnerNumberIsAvailable() throws Exception {
// given // given
when(partnerRepo.findPartnerByPartnerNumber(12345)).thenReturn(Optional.of(HsOfficePartnerEntity.builder() when(partnerRepo.findPartnerByPartnerNumber(12345)).thenReturn(Optional.of(HsOfficePartnerRbacEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.build())); .build()));

View File

@ -24,7 +24,7 @@ import static org.mockito.Mockito.lenient;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase< class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
HsOfficePartnerPatchResource, HsOfficePartnerPatchResource,
HsOfficePartnerEntity HsOfficePartnerRbacEntity
> { > {
private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID(); private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID();
@ -53,8 +53,8 @@ class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
} }
@Override @Override
protected HsOfficePartnerEntity newInitialEntity() { protected HsOfficePartnerRbacEntity newInitialEntity() {
final var entity = HsOfficePartnerEntity.builder() final var entity = HsOfficePartnerRbacEntity.builder()
.uuid(INITIAL_PARTNER_UUID) .uuid(INITIAL_PARTNER_UUID)
.partnerNumber(12345) .partnerNumber(12345)
.partnerRel(HsOfficeRelationRealEntity.builder() .partnerRel(HsOfficeRelationRealEntity.builder()
@ -72,7 +72,7 @@ class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
} }
@Override @Override
protected HsOfficePartnerEntityPatcher createPatcher(final HsOfficePartnerEntity partner) { protected HsOfficePartnerEntityPatcher createPatcher(final HsOfficePartnerRbacEntity partner) {
return new HsOfficePartnerEntityPatcher(em, partner); return new HsOfficePartnerEntityPatcher(em, partner);
} }
@ -83,7 +83,7 @@ class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
"partnerRel", "partnerRel",
HsOfficePartnerPatchResource::setPartnerRelUuid, HsOfficePartnerPatchResource::setPartnerRelUuid,
PATCHED_PARTNER_ROLE_UUID, PATCHED_PARTNER_ROLE_UUID,
HsOfficePartnerEntity::setPartnerRel, HsOfficePartnerRbacEntity::setPartnerRel,
newPartnerRel(PATCHED_PARTNER_ROLE_UUID)) newPartnerRel(PATCHED_PARTNER_ROLE_UUID))
.notNullable() .notNullable()
); );

View File

@ -12,7 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class HsOfficePartnerEntityUnitTest { class HsOfficePartnerEntityUnitTest {
private final HsOfficePartnerEntity givenPartner = HsOfficePartnerEntity.builder() private final HsOfficePartnerRbacEntity givenPartner = HsOfficePartnerRbacEntity.builder()
.partnerNumber(12345) .partnerNumber(12345)
.partnerRel(HsOfficeRelationRealEntity.builder() .partnerRel(HsOfficeRelationRealEntity.builder()
.anchor(HsOfficePersonRealEntity.builder() .anchor(HsOfficePersonRealEntity.builder()
@ -42,7 +42,7 @@ class HsOfficePartnerEntityUnitTest {
@Test @Test
void definesRbac() { void definesRbac() {
final var rbacFlowchart = new RbacViewMermaidFlowchartGenerator(HsOfficePartnerEntity.rbac()).toString(); final var rbacFlowchart = new RbacViewMermaidFlowchartGenerator(HsOfficePartnerRbacEntity.rbac()).toString();
assertThat(rbacFlowchart).isEqualTo(""" assertThat(rbacFlowchart).isEqualTo("""
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB flowchart TB

View File

@ -6,19 +6,19 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.grant.RawRbacGrantRepository; import net.hostsharing.hsadminng.rbac.grant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.role.RawRbacObjectRepository; import net.hostsharing.hsadminng.rbac.role.RawRbacObjectRepository;
import net.hostsharing.hsadminng.rbac.role.RawRbacRoleRepository; import net.hostsharing.hsadminng.rbac.role.RawRbacRoleRepository;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
@ -27,20 +27,21 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static net.hostsharing.hsadminng.mapper.Array.from;
import static net.hostsharing.hsadminng.rbac.grant.RawRbacGrantEntity.distinctGrantDisplaysOf; import static net.hostsharing.hsadminng.rbac.grant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.role.RawRbacObjectEntity.objectDisplaysOf; import static net.hostsharing.hsadminng.rbac.role.RawRbacObjectEntity.objectDisplaysOf;
import static net.hostsharing.hsadminng.rbac.role.RawRbacRoleEntity.distinctRoleNamesOf; import static net.hostsharing.hsadminng.rbac.role.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.hsadminng.mapper.Array.from;
import static net.hostsharing.hsadminng.rbac.role.RbacRoleType.ADMIN; import static net.hostsharing.hsadminng.rbac.role.RbacRoleType.ADMIN;
import static net.hostsharing.hsadminng.rbac.role.RbacRoleType.AGENT;
import static net.hostsharing.hsadminng.rbac.test.JpaAttempt.attempt; import static net.hostsharing.hsadminng.rbac.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest @DataJpaTest
@Import({ Context.class, JpaAttempt.class }) @Import({ Context.class, JpaAttempt.class })
class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithCleanup { class HsOfficePartnerRbacRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired @Autowired
HsOfficePartnerRepository partnerRepo; HsOfficePartnerRbacRepository partnerRepo;
@Autowired @Autowired
HsOfficeRelationRealRepository relationRepo; HsOfficeRelationRealRepository relationRepo;
@ -66,7 +67,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested
@ -80,8 +81,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var partnerRel = givenSomeTemporaryHostsharingPartnerRel("Winkler", "first contact"); final var partnerRel = givenSomeTemporaryHostsharingPartnerRel("Winkler", "first contact");
// when // when
final var result = attempt(em, () -> { final var result = attempt(
final var newPartner = HsOfficePartnerEntity.builder() em, () -> {
final var newPartner = HsOfficePartnerRbacEntity.builder()
.partnerNumber(20031) .partnerNumber(20031)
.partnerRel(partnerRel) .partnerRel(partnerRel)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
@ -91,7 +93,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then // then
result.assertSuccessful(); result.assertSuccessful();
assertThat(result.returnedValue()).isNotNull().extracting(HsOfficePartnerEntity::getUuid).isNotNull(); assertThat(result.returnedValue()).isNotNull().extracting(HsOfficePartnerRbacEntity::getUuid).isNotNull();
assertThatPartnerIsPersisted(result.returnedValue()); assertThatPartnerIsPersisted(result.returnedValue());
assertThat(partnerRepo.count()).isEqualTo(count + 1); assertThat(partnerRepo.count()).isEqualTo(count + 1);
} }
@ -108,7 +110,8 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.toList(); .toList();
// when // when
attempt(em, () -> { attempt(
em, () -> {
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenContact = contactrealRepo.findContactByOptionalCaptionLike("fourth contact").get(0); final var givenContact = contactrealRepo.findContactByOptionalCaptionLike("fourth contact").get(0);
final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0); final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
@ -121,7 +124,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.build(); .build();
relationRepo.save(newRelation); relationRepo.save(newRelation);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerRbacEntity.builder()
.partnerNumber(20032) .partnerNumber(20032)
.partnerRel(newRelation) .partnerRel(newRelation)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
@ -179,7 +182,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
null))); null)));
} }
private void assertThatPartnerIsPersisted(final HsOfficePartnerEntity saved) { private void assertThatPartnerIsPersisted(final HsOfficePartnerRbacEntity saved) {
final var found = partnerRepo.findByUuid(saved.getUuid()); final var found = partnerRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().extracting(Object::toString).isEqualTo(saved.toString()); assertThat(found).isNotEmpty().get().extracting(Object::toString).isEqualTo(saved.toString());
} }
@ -207,15 +210,32 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
} }
@Test @Test
public void normalUser_canViewOnlyRelatedPartners() { public void partnerAgent_canViewOnlyRelatedPartnersWithDetails() {
// given: // given:
context("person-FirstGmbH@example.com"); context(
"person-FirstGmbH@example.com",
"hs_office.relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT");
// when: // when:
final var result = partnerRepo.findPartnerByOptionalNameLike(null); final var result = partnerRepo.findPartnerByOptionalNameLike(null);
// then: // then:
exactlyThesePartnersAreReturned(result, "partner(P-10001: LP First GmbH, first contact)"); exactlyThesePartnersAreReturned(result,
"partner(P-10001: LP First GmbH, first contact)+(partnerDetails(Hamburg, RegNo123456789))");
}
@Test
public void partnerTenant_canViewRelatedPartnersButWithoutDetails() {
// given:
context(
"person-FirstGmbH@example.com",
"hs_office.relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT");
// when:
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
// then:
exactlyThesePartnersAreReturned(result, "partner(P-10001: LP First GmbH, first contact)+null");
} }
} }
@ -223,7 +243,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
class FindByNameLike { class FindByNameLike {
@Test @Test
public void globalAdmin_withoutAssumedRole_canViewAllPartners() { public void globalAdmin_withoutAssumedRole_canViewAllPartnersWithDetails() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
@ -231,7 +251,8 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var result = partnerRepo.findPartnerByOptionalNameLike("third contact"); final var result = partnerRepo.findPartnerByOptionalNameLike("third contact");
// then // then
exactlyThesePartnersAreReturned(result, "partner(P-10003: IF Third OHG, third contact)"); exactlyThesePartnersAreReturned(result,
"partner(P-10003: IF Third OHG, third contact)+(partnerDetails(Hamburg, RegNo123456789))");
} }
} }
@ -289,19 +310,20 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
} }
@Test @Test
public void partnerRelationAgent_canUpdateRelatedPartner() { public void partnerRelationAgent_canUpdateRelatedPartnerDetails() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth"); final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office.person#ErbenBesslerMelBessler:ADMIN"); givenPartner.getPartnerRel().roleId(AGENT));
assertThatPartnerActuallyInDatabase(givenPartner); assertThatPartnerActuallyInDatabase(givenPartner);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", context(
"hs_office.person#ErbenBesslerMelBessler:ADMIN"); "superuser-alex@hostsharing.net",
givenPartner.getPartnerRel().roleId(AGENT));
givenPartner.getDetails().setBirthName("new birthname"); givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner); return partnerRepo.save(givenPartner);
}); });
@ -310,37 +332,17 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful(); result.assertSuccessful();
} }
@Test private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerRbacEntity saved) {
public void partnerRelationTenant_canNotUpdateRelatedPartner() {
// given
context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
"hs_office.person#ErbenBesslerMelBessler:ADMIN");
assertThatPartnerActuallyInDatabase(givenPartner);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net",
"hs_office.relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:TENANT");
givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner);
});
// then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
"ERROR: [403] insert into hs_office.partner_details ",
" not allowed for current subjects {hs_office.relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:TENANT}");
}
private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerEntity saved) {
final var found = partnerRepo.findByUuid(saved.getUuid()); final var found = partnerRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().isNotSameAs(saved).extracting(HsOfficePartnerEntity::toString).isEqualTo(saved.toString()); assertThat(found).isNotEmpty()
.get()
.isNotSameAs(saved)
.extracting(HsOfficePartnerRbacEntity::toString)
.isEqualTo(saved.toString());
} }
private void assertThatPartnerIsVisibleForUserWithRole( private void assertThatPartnerIsVisibleForUserWithRole(
final HsOfficePartnerEntity entity, final HsOfficePartnerRbacEntity entity,
final String assumedRoles) { final String assumedRoles) {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles); context("superuser-alex@hostsharing.net", assumedRoles);
@ -349,7 +351,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
} }
private void assertThatPartnerIsNotVisibleForUserWithRole( private void assertThatPartnerIsNotVisibleForUserWithRole(
final HsOfficePartnerEntity entity, final HsOfficePartnerRbacEntity entity,
final String assumedRoles) { final String assumedRoles) {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles); context("superuser-alex@hostsharing.net", assumedRoles);
@ -451,19 +453,22 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
"[creating partner test-data , hs_office.partner, INSERT, 10010]"); "[creating partner test-data , hs_office.partner, INSERT, 10010]");
} }
private HsOfficePartnerEntity givenSomeTemporaryHostsharingPartner( private HsOfficePartnerRbacEntity givenSomeTemporaryHostsharingPartner(
final Integer partnerNumber, final String person, final String contact) { final Integer partnerNumber, final String person, final String contact) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var partnerRel = givenSomeTemporaryHostsharingPartnerRel(person, contact); final var partnerRel = givenSomeTemporaryHostsharingPartnerRel(person, contact);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerRbacEntity.builder()
.partnerNumber(partnerNumber) .partnerNumber(partnerNumber)
.partnerRel(partnerRel) .partnerRel(partnerRel)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
.build(); .build();
return partnerRepo.save(newPartner); final var savedPartner = partnerRepo.save(newPartner);
em.flush();
final var partner = em.find(savedPartner.getClass(), savedPartner.getUuid());
return savedPartner;
}).assertSuccessful().returnedValue(); }).assertSuccessful().returnedValue();
} }
@ -482,21 +487,23 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
return partnerRel; return partnerRel;
} }
void exactlyThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) { void exactlyThesePartnersAreReturned(final List<HsOfficePartnerRbacEntity> actualResult, final String... partnerNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(partnerEntity -> partnerEntity.toString()) .extracting(partner ->
partner.toString() + "+" +
(partner.getDetails() != null ? ("(" + partner.getDetails() + ")") : "null"))
.containsExactlyInAnyOrder(partnerNames); .containsExactlyInAnyOrder(partnerNames);
} }
void allThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) { void allThesePartnersAreReturned(final List<HsOfficePartnerRbacEntity> actualResult, final String... partnerNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(partnerEntity -> partnerEntity.toString()) .extracting(HsOfficePartnerRbacEntity::toString)
.contains(partnerNames); .contains(partnerNames);
} }
@AfterEach @AfterEach
void cleanup() { void cleanup() {
cleanupAllNew(HsOfficePartnerEntity.class); cleanupAllNew(HsOfficePartnerRbacEntity.class);
} }
private String[] distinct(final String[] strings) { private String[] distinct(final String[] strings) {

View File

@ -7,12 +7,12 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGAL_PERSON; import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGAL_PERSON;
public class TestHsOfficePartner { public class HsOfficeTestRealPartner {
public static final HsOfficePartnerEntity TEST_PARTNER = hsOfficePartnerWithLegalPerson("Test Ltd."); public static final HsOfficePartnerRealEntity TEST_PARTNER = hsOfficePartnerWithLegalPerson("Test Ltd.");
static public HsOfficePartnerEntity hsOfficePartnerWithLegalPerson(final String tradeName) { static public HsOfficePartnerRealEntity hsOfficePartnerWithLegalPerson(final String tradeName) {
return HsOfficePartnerEntity.builder() return HsOfficePartnerRealEntity.builder()
.partnerNumber(10001) .partnerNumber(10001)
.partnerRel( .partnerRel(
HsOfficeRelationRealEntity.builder() HsOfficeRelationRealEntity.builder()

View File

@ -331,7 +331,6 @@ class HsOfficePersonControllerAcceptanceTest extends ContextBasedTestWithCleanup
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.define(creatingUser); context.define(creatingUser);
final var newPerson = HsOfficePersonRealEntity.builder() final var newPerson = HsOfficePersonRealEntity.builder()
.uuid(UUID.randomUUID())
.personType(HsOfficePersonType.LEGAL_PERSON) .personType(HsOfficePersonType.LEGAL_PERSON)
.tradeName("Temp " + Context.getCallerMethodNameFromStackFrame(2)) .tradeName("Temp " + Context.getCallerMethodNameFromStackFrame(2))
.familyName(RandomStringUtils.randomAlphabetic(10) + "@example.org") .familyName(RandomStringUtils.randomAlphabetic(10) + "@example.org")

View File

@ -11,7 +11,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -46,7 +46,7 @@ class HsOfficePersonRbacRepositoryIntegrationTest extends ContextBasedTestWithCl
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -10,7 +10,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -44,7 +44,7 @@ class HsOfficePersonRealRepositoryIntegrationTest extends ContextBasedTestWithCl
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Autowired @Autowired
private HsOfficePersonRealRepository hsOfficePersonRealRepository; private HsOfficePersonRealRepository hsOfficePersonRealRepository;

View File

@ -9,7 +9,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -35,7 +35,7 @@ class HsOfficeRealRelationRepositoryIntegrationTest extends ContextBasedTestWith
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -13,7 +13,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
@ -58,7 +58,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

View File

@ -180,10 +180,9 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
void globalAdmin_canNotPostNewSepaMandateWhenDebitorUuidIsMissing() { void globalAdmin_canNotPostNewSepaMandateWhenDebitorUuidIsMissing() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("Third").get(0);
final var givenBankAccount = bankAccountRepo.findByIbanOrderByIbanAsc("DE02200505501015871393").get(0); final var givenBankAccount = bankAccountRepo.findByIbanOrderByIbanAsc("DE02200505501015871393").get(0);
final var location = RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
@ -227,12 +226,12 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.post("http://localhost/api/hs/office/sepamandates") .post("http://localhost/api/hs/office/sepamandates")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(400) .statusCode(400)
.body("message", is("ERROR: [400] Unable to find BankAccount with uuid 00000000-0000-0000-0000-000000000000")); .body("message", is("ERROR: [400] bankAccount.uuid='00000000-0000-0000-0000-000000000000' not found or not accessible"));
// @formatter:on // @formatter:on
} }
@Test @Test
void globalAdmin_canNotPostNewSepaMandate_ifPersonDoesNotExist() { void globalAdmin_canNotPostNewSepaMandate_ifDebitorDoesNotExist() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenDebitorUuid = UUID.fromString("00000000-0000-0000-0000-000000000000"); final var givenDebitorUuid = UUID.fromString("00000000-0000-0000-0000-000000000000");
@ -257,7 +256,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.post("http://localhost/api/hs/office/sepamandates") .post("http://localhost/api/hs/office/sepamandates")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(400) .statusCode(400)
.body("message", is("ERROR: [400] Unable to find Debitor with uuid 00000000-0000-0000-0000-000000000000")); .body("message", is("ERROR: [400] debitor.uuid='00000000-0000-0000-0000-000000000000' not found or not accessible"));
// @formatter:on // @formatter:on
} }
} }
@ -529,7 +528,6 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.orElse(givenDebitor.getPartner().getPartnerRel().getHolder().getFamilyName()); .orElse(givenDebitor.getPartner().getPartnerRel().getHolder().getFamilyName());
final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike(bankAccountHolder).get(0); final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike(bankAccountHolder).get(0);
final var newSepaMandate = HsOfficeSepaMandateEntity.builder() final var newSepaMandate = HsOfficeSepaMandateEntity.builder()
.uuid(UUID.randomUUID())
.debitor(givenDebitor) .debitor(givenDebitor)
.bankAccount(givenBankAccount) .bankAccount(givenBankAccount)
.reference("temp ref CAT Z") .reference("temp ref CAT Z")

View File

@ -13,7 +13,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
@ -55,7 +55,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@MockBean @MockitoBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested

Some files were not shown because too many files have changed in this diff Show More