upgrade to Spring Boot 3.4.1 and fixed most issues, except one particular strange RBAC problem
This commit is contained in:
parent
a7ffee9348
commit
eba39ff27a
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
10
build.gradle
10
build.gradle
@ -1,11 +1,11 @@
|
|||||||
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 "11.1.1"
|
||||||
id "com.diffplug.spotless" version "7.0.0"
|
id "com.diffplug.spotless" version "7.0.1"
|
||||||
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'
|
||||||
@ -20,6 +20,8 @@ wrapper {
|
|||||||
gradleVersion = '8.5'
|
gradleVersion = '8.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build what is described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
compileOnly {
|
compileOnly {
|
||||||
extendsFrom annotationProcessor
|
extendsFrom annotationProcessor
|
||||||
@ -61,7 +63,7 @@ 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.1'
|
||||||
implementation 'org.postgresql:postgresql:42.7.4'
|
implementation 'org.postgresql:postgresql:42.7.4'
|
||||||
implementation 'org.liquibase:liquibase-core:4.30.0'
|
implementation 'org.liquibase:liquibase-core:4.30.0'
|
||||||
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
|
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
|
||||||
@ -71,7 +73,7 @@ dependencies {
|
|||||||
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.1'
|
||||||
implementation 'org.reflections:reflections:0.10.2'
|
implementation 'org.reflections:reflections:0.10.2'
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
|
@ -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"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -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()
|
.getAllValidationResults() // FIXME: deprecated
|
||||||
.stream()
|
.stream()
|
||||||
.map(ParameterValidationResult::getResolvableErrors)
|
.map(ParameterValidationResult::getResolvableErrors)
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
|
@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
|||||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
|
import jakarta.persistence.FlushModeType;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
||||||
@ -53,6 +54,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) {
|
private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) {
|
||||||
|
em.setFlushMode(FlushModeType.COMMIT); // FIXME: check and remove or reset
|
||||||
final Object result = em.createNativeQuery("SELECT nextval('hs_hosting.asset_unixuser_system_id_seq')", Integer.class)
|
final Object result = em.createNativeQuery("SELECT nextval('hs_hosting.asset_unixuser_system_id_seq')", Integer.class)
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
return (Integer) result;
|
return (Integer) result;
|
||||||
|
@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopShar
|
|||||||
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.errors.MultiValidationException;
|
||||||
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;
|
||||||
@ -33,14 +33,13 @@ 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;
|
||||||
|
|
||||||
@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,
|
||||||
|
@ -2,13 +2,14 @@ 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.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.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 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;
|
||||||
@ -36,13 +37,16 @@ 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
|
||||||
|
private HsOfficeBankAccountRepository bankAccountRepo;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EntityExistsValidator entityValidator;
|
private EntityExistsValidator entityValidator;
|
||||||
@ -88,18 +92,18 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
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) {
|
if (body.getDebitorRel() != null) { // FIXME: move this into RESOURCE_TO_ENTITY_POSTMAPPER
|
||||||
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
||||||
debitorRel.setType(DEBITOR);
|
debitorRel.setType(DEBITOR);
|
||||||
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
||||||
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
||||||
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
||||||
entityToSave.setDebitorRel(relrealRepo.save(debitorRel));
|
entityToSave.setDebitorRel(realRelRepo.save(debitorRel));
|
||||||
} else {
|
} else {
|
||||||
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
final var debitorRelOptional = realRelRepo.findByUuid(body.getDebitorRelUuid());
|
||||||
debitorRelOptional.ifPresentOrElse(
|
debitorRelOptional.ifPresentOrElse(
|
||||||
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
|
debitorRel -> {entityToSave.setDebitorRel(realRelRepo.save(debitorRel));},
|
||||||
() -> {
|
() -> {
|
||||||
throw new ValidationException(
|
throw new ValidationException(
|
||||||
"Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());
|
"Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());
|
||||||
@ -107,7 +111,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final var savedEntity = debitorRepo.save(entityToSave);
|
final var savedEntity = debitorRepo.save(entityToSave);
|
||||||
em.flush();
|
em.flush(); // FIXME: necessary?
|
||||||
em.refresh(savedEntity);
|
em.refresh(savedEntity);
|
||||||
|
|
||||||
final var uri =
|
final var uri =
|
||||||
@ -191,6 +195,14 @@ 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.getRefundBankAccountUuid() != null) {
|
||||||
|
final var bankAccountEntity = bankAccountRepo.findByUuid(resource.getRefundBankAccountUuid())
|
||||||
|
.orElseThrow(() -> new ValidationException()); // FIXME
|
||||||
|
entity.setRefundBankAccount(bankAccountEntity);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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());
|
||||||
};
|
};
|
||||||
|
@ -7,13 +7,15 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMember
|
|||||||
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.HsOfficePartnerEntity;
|
||||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
|
||||||
|
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 HsOfficePartnerRepository partnerRepo;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeMembershipRepository membershipRepo;
|
private HsOfficeMembershipRepository membershipRepo;
|
||||||
@ -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()); // FIXME: 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()))));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -16,18 +16,33 @@ public interface HsOfficePartnerRepository extends Repository<HsOfficePartnerEnt
|
|||||||
@Timed("app.office.partners.repo.findAll")
|
@Timed("app.office.partners.repo.findAll")
|
||||||
List<HsOfficePartnerEntity> findAll(); // TODO.refa: move to a repo in test sources
|
List<HsOfficePartnerEntity> findAll(); // TODO.refa: move to a repo in test sources
|
||||||
|
|
||||||
@Query("""
|
// @Query("""
|
||||||
SELECT partner FROM HsOfficePartnerEntity partner
|
// SELECT partner FROM HsOfficePartnerEntity partner
|
||||||
JOIN HsOfficeRelationRealEntity rel ON rel.uuid = partner.partnerRel.uuid
|
// JOIN HsOfficeRelationRealEntity rel ON rel.uuid = partner.partnerRel.uuid
|
||||||
JOIN HsOfficeContactRealEntity contact ON contact.uuid = rel.contact.uuid
|
// JOIN HsOfficeContactRealEntity contact ON contact.uuid = rel.contact.uuid
|
||||||
JOIN HsOfficePersonRealEntity person ON person.uuid = rel.holder.uuid
|
// JOIN HsOfficePersonRealEntity person ON person.uuid = rel.holder.uuid
|
||||||
WHERE :name is null
|
// LEFT JOIN HsOfficePartnerDetailsEntity details ON partner.details.uuid = details.uuid
|
||||||
OR partner.details.birthName like concat(cast(:name as text), '%')
|
// WHERE :name is null
|
||||||
OR contact.caption like concat(cast(:name as text), '%')
|
// OR (details IS NOT NULL AND details.birthName like concat(cast(:name as text), '%'))
|
||||||
OR person.tradeName like concat(cast(:name as text), '%')
|
// OR contact.caption like concat(cast(:name as text), '%')
|
||||||
OR person.givenName like concat(cast(:name as text), '%')
|
// OR person.tradeName like concat(cast(:name as text), '%')
|
||||||
OR person.familyName like concat(cast(:name as text), '%')
|
// OR person.givenName like concat(cast(:name as text), '%')
|
||||||
""")
|
// OR person.familyName like concat(cast(:name as text), '%')
|
||||||
|
// """)
|
||||||
|
@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")
|
@Timed("app.office.partners.repo.findPartnerByOptionalNameLike")
|
||||||
List<HsOfficePartnerEntity> findPartnerByOptionalNameLike(String name);
|
List<HsOfficePartnerEntity> findPartnerByOptionalNameLike(String name);
|
||||||
|
|
||||||
|
@ -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> {
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
)
|
||||||
|
));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)),
|
||||||
|
@ -197,7 +197,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", "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("...", ...)
|
||||||
@ -214,46 +214,46 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
|
|
||||||
final var location = RestAssured // @formatter:off
|
final var location = 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)
|
||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
"membership.uuid": "%s",
|
"membership.uuid": "%s",
|
||||||
"transactionType": "REVERSAL",
|
"transactionType": "REVERSAL",
|
||||||
"shareCount": %s,
|
"shareCount": %s,
|
||||||
"valueDate": "2022-10-30",
|
"valueDate": "2022-10-30",
|
||||||
"reference": "test reversal ref",
|
"reference": "test reversal ref",
|
||||||
"comment": "some coop shares reversal transaction",
|
"comment": "some coop shares reversal transaction",
|
||||||
"revertedShareTx.uuid": "%s"
|
"revertedShareTx.uuid": "%s"
|
||||||
}
|
}
|
||||||
""".formatted(
|
""".formatted(
|
||||||
givenMembership.getUuid(),
|
givenMembership.getUuid(),
|
||||||
-givenTransaction.getShareCount(),
|
-givenTransaction.getShareCount(),
|
||||||
givenTransaction.getUuid()))
|
givenTransaction.getUuid()))
|
||||||
.port(port)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.post("http://localhost/api/hs/office/coopsharestransactions")
|
.post("http://localhost/api/hs/office/coopsharestransactions")
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(201)
|
.statusCode(201)
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.body("uuid", isUuidValid())
|
.body("uuid", isUuidValid())
|
||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
{
|
{
|
||||||
"transactionType": "REVERSAL",
|
"transactionType": "REVERSAL",
|
||||||
"shareCount": -13,
|
"shareCount": -13,
|
||||||
"valueDate": "2022-10-30",
|
"valueDate": "2022-10-30",
|
||||||
"reference": "test reversal ref",
|
"reference": "test reversal ref",
|
||||||
"comment": "some coop shares reversal transaction",
|
"comment": "some coop shares reversal transaction",
|
||||||
"revertedShareTx": {
|
"revertedShareTx": {
|
||||||
"transactionType": "SUBSCRIPTION",
|
"transactionType": "SUBSCRIPTION",
|
||||||
"shareCount": 13,
|
"shareCount": 13,
|
||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
"reference": "test ref"
|
"reference": "test ref"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"""))
|
}
|
||||||
.header("Location", startsWith("http://localhost"))
|
"""))
|
||||||
.extract().header("Location"); // @formatter:on
|
.header("Location", startsWith("http://localhost"))
|
||||||
|
.extract().header("Location"); // @formatter:on
|
||||||
|
|
||||||
// finally, the new coopAssetsTransaction can be accessed under the generated UUID
|
// finally, the new coopAssetsTransaction can be accessed under the generated UUID
|
||||||
final var newShareTxUuid = UUID.fromString(
|
final var newShareTxUuid = UUID.fromString(
|
||||||
@ -269,22 +269,34 @@ 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")
|
||||||
"membership.uuid": "%s",
|
.contentType(ContentType.JSON)
|
||||||
"transactionType": "CANCELLATION",
|
.body("""
|
||||||
"shareCount": -80,
|
|
||||||
"valueDate": "2022-10-13",
|
|
||||||
"reference": "temp ref X",
|
|
||||||
"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("""
|
|
||||||
{
|
{
|
||||||
"statusCode": 400,
|
"membership.uuid": "%s",
|
||||||
"statusPhrase": "Bad Request",
|
"transactionType": "CANCELLATION",
|
||||||
"message": "ERROR: [400] coop shares transaction would result in a negative number of shares"
|
"shareCount": -80,
|
||||||
}
|
"valueDate": "2022-10-13",
|
||||||
""")); // @formatter:on
|
"reference": "temp ref X",
|
||||||
|
"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("""
|
||||||
|
{
|
||||||
|
"statusCode": 400,
|
||||||
|
"statusPhrase": "Bad Request",
|
||||||
|
"message": "ERROR: [400] coop shares transaction would result in a negative number of shares"
|
||||||
|
}
|
||||||
|
""")); // @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.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;
|
||||||
@ -36,7 +36,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
|
|||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
@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
|
@MockBean
|
||||||
HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
||||||
|
@ -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")))
|
||||||
|
@ -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;
|
||||||
@ -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() {
|
||||||
|
@ -16,7 +16,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.boot.test.mock.mockito.MockBean; // FIXME: use 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;
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ import static net.hostsharing.hsadminng.rbac.role.RawRbacObjectEntity.objectDisp
|
|||||||
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.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;
|
||||||
|
|
||||||
@ -207,9 +208,23 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void normalUser_canViewOnlyRelatedPartners() {
|
public void partnerAgent_canViewOnlyRelatedPartnersWithoutDetails() {
|
||||||
// given:
|
// given:
|
||||||
context("person-FirstGmbH@example.com");
|
context("person-FirstGmbH@example.com",
|
||||||
|
"hs_office.relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT");
|
||||||
|
|
||||||
|
// when:
|
||||||
|
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
|
||||||
|
|
||||||
|
// then:
|
||||||
|
exactlyThesePartnersAreReturned(result, "partner(P-10001: LP First GmbH, first contact)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void partnerTenant_canViewRelatedPartnersButWithoutDetails() {
|
||||||
|
// given:
|
||||||
|
context("person-FirstGmbH@example.com",
|
||||||
|
"hs_office.relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT");
|
||||||
|
|
||||||
// when:
|
// when:
|
||||||
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
|
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
|
||||||
@ -289,19 +304,19 @@ 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("superuser-alex@hostsharing.net",
|
||||||
"hs_office.person#ErbenBesslerMelBessler:ADMIN");
|
givenPartner.getPartnerRel().roleId(AGENT));
|
||||||
givenPartner.getDetails().setBirthName("new birthname");
|
givenPartner.getDetails().setBirthName("new birthname");
|
||||||
return partnerRepo.save(givenPartner);
|
return partnerRepo.save(givenPartner);
|
||||||
});
|
});
|
||||||
@ -310,30 +325,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
result.assertSuccessful();
|
result.assertSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
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) {
|
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(HsOfficePartnerEntity::toString).isEqualTo(saved.toString());
|
||||||
@ -463,7 +454,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,13 +478,13 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
|
|
||||||
void exactlyThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
void exactlyThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
||||||
assertThat(actualResult)
|
assertThat(actualResult)
|
||||||
.extracting(partnerEntity -> partnerEntity.toString())
|
.extracting(HsOfficePartnerEntity::toString)
|
||||||
.containsExactlyInAnyOrder(partnerNames);
|
.containsExactlyInAnyOrder(partnerNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void allThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
void allThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {
|
||||||
assertThat(actualResult)
|
assertThat(actualResult)
|
||||||
.extracting(partnerEntity -> partnerEntity.toString())
|
.extracting(HsOfficePartnerEntity::toString)
|
||||||
.contains(partnerNames);
|
.contains(partnerNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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")
|
||||||
|
@ -6,7 +6,7 @@ management:
|
|||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
include: info, health, metrics, metric-links
|
include: info, health, metrics, metric-links, mappings, openapi, swaggerui
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
sql:
|
sql:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user