spring-boot-3-2-upgrade (#32)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #32 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
parent
ad04faa21d
commit
73c378b456
34
build.gradle
34
build.gradle
@ -1,15 +1,15 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.1.7'
|
id 'org.springframework.boot' version '3.2.4'
|
||||||
id 'io.spring.dependency-management' version '1.1.4'
|
id 'io.spring.dependency-management' version '1.1.4'
|
||||||
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.5'
|
id 'com.github.jk1.dependency-license-report' version '2.6'
|
||||||
id "org.owasp.dependencycheck" version "9.0.7"
|
id "org.owasp.dependencycheck" version "9.0.10"
|
||||||
id "com.diffplug.spotless" version "6.23.3"
|
id "com.diffplug.spotless" version "6.25.0"
|
||||||
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.50.0'
|
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'net.hostsharing'
|
group = 'net.hostsharing'
|
||||||
@ -59,28 +59,16 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.1'
|
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.1'
|
||||||
implementation 'org.springdoc:springdoc-openapi:2.3.0'
|
implementation 'org.springdoc:springdoc-openapi:2.4.0'
|
||||||
implementation 'org.postgresql:postgresql:42.7.1'
|
implementation 'org.postgresql:postgresql:42.7.3'
|
||||||
implementation 'org.liquibase:liquibase-core:4.25.1'
|
implementation 'org.liquibase:liquibase-core:4.27.0'
|
||||||
implementation 'com.vladmihalcea:hibernate-types-60:2.21.1'
|
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.3'
|
||||||
implementation 'io.hypersistence:hypersistence-utils-hibernate-62:3.7.0'
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0'
|
||||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1'
|
|
||||||
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.11.0'
|
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||||
implementation 'org.modelmapper:modelmapper:3.2.0'
|
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||||
implementation 'org.iban4j:iban4j:3.2.7-RELEASE'
|
implementation 'org.iban4j:iban4j:3.2.7-RELEASE'
|
||||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0'
|
||||||
|
|
||||||
// fixes vulnerability CVE-2022-1471
|
|
||||||
// The dependency usually comes from Spring Boot, just in the wrong version.
|
|
||||||
// TODO: Remove this explicit dependency once we are on SpringBoot 3.2.x
|
|
||||||
// as well as the related exclude in settings.gradle
|
|
||||||
// and the dependency suppression in owasp-dependency-check-suppression.xml.
|
|
||||||
implementation('org.yaml:snakeyaml') {
|
|
||||||
version {
|
|
||||||
strictly('2.2')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
testCompileOnly 'org.projectlombok:lombok'
|
testCompileOnly 'org.projectlombok:lombok'
|
||||||
|
@ -694,7 +694,7 @@ Users can view only the roles to which are granted to them.
|
|||||||
|
|
||||||
Grant can be `empowered`, this means that the grantee user can grant the granted role to other users
|
Grant can be `empowered`, this means that the grantee user can grant the granted role to other users
|
||||||
and revoke grants to that role.
|
and revoke grants to that role.
|
||||||
(TODO: access control part not yet implemented)
|
(TODO: access control part not yet implemented, currently all accessible roles can be granted to other users)
|
||||||
|
|
||||||
Grants can be `managed`, which means they are created and deleted by system-defined rules.
|
Grants can be `managed`, which means they are created and deleted by system-defined rules.
|
||||||
If a grant is not managed, it was created by an empowered user and can be deleted by empowered users.
|
If a grant is not managed, it was created by an empowered user and can be deleted by empowered users.
|
||||||
|
@ -1,33 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
|
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
|
||||||
<suppress>
|
|
||||||
<notes><![CDATA[
|
|
||||||
We don't use the Spring HTTP invoker which causes this vulnerability due to Java deserialization.
|
|
||||||
]]></notes>
|
|
||||||
<packageUrl regex="true">^pkg:maven/org\.springframework/spring-web@.*$</packageUrl>
|
|
||||||
<cve>CVE-2016-1000027</cve>
|
|
||||||
</suppress>
|
|
||||||
<suppress>
|
|
||||||
<notes><![CDATA[
|
|
||||||
We don't use the UNWRAP_SINGLE_VALUE_ARRAYS feature and thus are not affected.
|
|
||||||
]]></notes>
|
|
||||||
<packageUrl regex="true">^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$</packageUrl>
|
|
||||||
<cve>CVE-2022-42003</cve>
|
|
||||||
</suppress>
|
|
||||||
<suppress>
|
|
||||||
<notes><![CDATA[
|
|
||||||
We don't parse external XML.
|
|
||||||
]]></notes>
|
|
||||||
<packageUrl regex="true">^pkg:maven/org\.eclipse\.angus/angus\-activation@.*$</packageUrl>
|
|
||||||
<cpe>cpe:/a:eclipse:eclipse_ide</cpe>
|
|
||||||
</suppress>
|
|
||||||
<suppress>
|
|
||||||
<notes><![CDATA[
|
|
||||||
We don't parse external XML.
|
|
||||||
]]></notes>
|
|
||||||
<packageUrl regex="true">^pkg:maven/jakarta\.activation/jakarta\.activation\-api@.*$</packageUrl>
|
|
||||||
<cpe>cpe:/a:eclipse:eclipse_ide</cpe>
|
|
||||||
</suppress>
|
|
||||||
<suppress>
|
<suppress>
|
||||||
<notes><![CDATA[
|
<notes><![CDATA[
|
||||||
Cyclic references are not possible if file comes in JSON text format.
|
Cyclic references are not possible if file comes in JSON text format.
|
||||||
@ -35,13 +7,6 @@
|
|||||||
<packageUrl regex="true">^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$</packageUrl>
|
<packageUrl regex="true">^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$</packageUrl>
|
||||||
<cpe>cpe:/a:fasterxml:jackson-databind</cpe>
|
<cpe>cpe:/a:fasterxml:jackson-databind</cpe>
|
||||||
</suppress>
|
</suppress>
|
||||||
<suppress>
|
|
||||||
<notes><![CDATA[
|
|
||||||
As far as I see Criteria.parse(...) cannot be reached with external data.
|
|
||||||
]]></notes>
|
|
||||||
<packageUrl regex="true">^pkg:maven/com\.jayway\.jsonpath/json\-path@.*$</packageUrl>
|
|
||||||
<vulnerabilityName>CVE-2023-51074</vulnerabilityName>
|
|
||||||
</suppress>
|
|
||||||
<suppress>
|
<suppress>
|
||||||
<notes><![CDATA[
|
<notes><![CDATA[
|
||||||
Internal tooling, not exposed to the Internet.
|
Internal tooling, not exposed to the Internet.
|
||||||
@ -49,17 +14,4 @@
|
|||||||
<packageUrl regex="true">^pkg:maven/org\.pitest/pitest\-command\-line@.*$</packageUrl>
|
<packageUrl regex="true">^pkg:maven/org\.pitest/pitest\-command\-line@.*$</packageUrl>
|
||||||
<cpe>cpe:/a:line:line</cpe>
|
<cpe>cpe:/a:line:line</cpe>
|
||||||
</suppress>
|
</suppress>
|
||||||
<suppress>
|
|
||||||
<notes><![CDATA[
|
|
||||||
Spring Boot 3.1.x has a transient dependency to snakeyaml 1.3
|
|
||||||
which contains this vulnerability.
|
|
||||||
|
|
||||||
We've explicitly bumped to 2.2, but the vulnerability checker does not seem to notice that.
|
|
||||||
|
|
||||||
TODO: Remove this suppression once we are on SpringBoot 3.2,
|
|
||||||
as well as the explicit version bump and the transient dependency exclude.
|
|
||||||
]]></notes>
|
|
||||||
<packageUrl regex="true">^pkg:maven/org\.yaml/snakeyaml@.*$</packageUrl>
|
|
||||||
<cve>CVE-2022-1471</cve>
|
|
||||||
</suppress>
|
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
@ -11,28 +11,4 @@ plugins {
|
|||||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
|
||||||
components {
|
|
||||||
all {
|
|
||||||
allVariants {
|
|
||||||
withDependencies {
|
|
||||||
removeAll {
|
|
||||||
// Spring Boot 3.1.x has a transient dependency to snakeyaml 1.3
|
|
||||||
// which contains a severe vulnerability.
|
|
||||||
// Here we remove this transient dependency and in build.gradle
|
|
||||||
// we add an explicit dependency to snakeyaml 2.2,
|
|
||||||
// which does not have this vulnerability anymore.
|
|
||||||
//
|
|
||||||
// TODO: Check Once we are on SpringBoot 3.2.x, check if this exclude
|
|
||||||
// is still neccessary. If not:
|
|
||||||
// Remove it // as well as the related explicit dependency in build.gradle
|
|
||||||
// and the dependency suppression in owasp-dependency-check-suppression.xml.
|
|
||||||
it.module in [ 'snakeyaml' ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.name = 'hsadmin-ng'
|
rootProject.name = 'hsadmin-ng'
|
||||||
|
@ -15,11 +15,9 @@ import java.util.Collections;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static java.util.function.Predicate.not;
|
import static java.util.function.Predicate.not;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresArray.fromPostgresArray;
|
|
||||||
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
|
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -82,14 +80,11 @@ public class Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String[] getAssumedRoles() {
|
public String[] getAssumedRoles() {
|
||||||
final byte[] result = (byte[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
|
return (String[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
|
||||||
return fromPostgresArray(result, String.class, Function.identity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID[] currentSubjectsUuids() {
|
public UUID[] currentSubjectsUuids() {
|
||||||
final byte[] result = (byte[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class)
|
return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
|
||||||
.getSingleResult();
|
|
||||||
return fromPostgresArray(result, UUID.class, UUID::fromString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCallerMethodNameFromStackFrame(final int skipFrames) {
|
public static String getCallerMethodNameFromStackFrame(final int skipFrames) {
|
||||||
|
@ -11,16 +11,18 @@ import org.springframework.http.converter.HttpMessageNotReadableException;
|
|||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
|
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
|
||||||
import org.springframework.orm.jpa.JpaSystemException;
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.validation.method.ParameterValidationResult;
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.method.annotation.HandlerMethodValidationException;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
|
||||||
import jakarta.persistence.EntityNotFoundException;
|
import jakarta.persistence.EntityNotFoundException;
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.errors.CustomErrorResponse.*;
|
import static net.hostsharing.hsadminng.errors.CustomErrorResponse.*;
|
||||||
@ -119,6 +121,28 @@ public class RestResponseEntityExceptionHandler
|
|||||||
return errorResponse(request, HttpStatus.BAD_REQUEST, errorList.toString());
|
return errorResponse(request, HttpStatus.BAD_REQUEST, errorList.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked,rawtypes")
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity handleHandlerMethodValidationException(
|
||||||
|
final HandlerMethodValidationException exc,
|
||||||
|
final HttpHeaders headers,
|
||||||
|
final HttpStatusCode status,
|
||||||
|
final WebRequest request) {
|
||||||
|
final var errorList = exc
|
||||||
|
.getAllValidationResults()
|
||||||
|
.stream()
|
||||||
|
.map(ParameterValidationResult::getResolvableErrors)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.filter(FieldError.class::isInstance)
|
||||||
|
.map(FieldError.class::cast)
|
||||||
|
.map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage() + " but is \""
|
||||||
|
+ fieldError.getRejectedValue() + "\"")
|
||||||
|
.toList();
|
||||||
|
return errorResponse(request, HttpStatus.BAD_REQUEST, errorList.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private String userReadableEntityClassName(final String exceptionMessage) {
|
private String userReadableEntityClassName(final String exceptionMessage) {
|
||||||
final var regex = "(net.hostsharing.hsadminng.[a-z0-9_.]*.[A-Za-z0-9_$]*Entity) ";
|
final var regex = "(net.hostsharing.hsadminng.[a-z0-9_.]*.[A-Za-z0-9_$]*Entity) ";
|
||||||
final var pattern = Pattern.compile(regex);
|
final var pattern = Pattern.compile(regex);
|
||||||
|
@ -13,7 +13,6 @@ 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.validation.Valid;
|
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -59,7 +58,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction(
|
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction(
|
||||||
final String currentUser,
|
final String currentUser,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
@Valid final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
||||||
|
|
||||||
context.define(currentUser, assumedRoles);
|
context.define(currentUser, assumedRoles);
|
||||||
validate(requestBody);
|
validate(requestBody);
|
||||||
|
@ -10,7 +10,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
|
|||||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
@ -13,7 +13,6 @@ 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.validation.Valid;
|
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -60,7 +59,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction(
|
public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction(
|
||||||
final String currentUser,
|
final String currentUser,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
@Valid final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
||||||
|
|
||||||
context.define(currentUser, assumedRoles);
|
context.define(currentUser, assumedRoles);
|
||||||
validate(requestBody);
|
validate(requestBody);
|
||||||
|
@ -7,10 +7,8 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
@ -12,7 +12,6 @@ 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.validation.Valid;
|
|
||||||
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;
|
||||||
@ -53,7 +52,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
public ResponseEntity<HsOfficeMembershipResource> addMembership(
|
public ResponseEntity<HsOfficeMembershipResource> addMembership(
|
||||||
final String currentUser,
|
final String currentUser,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
@Valid final HsOfficeMembershipInsertResource body) {
|
final HsOfficeMembershipInsertResource body) {
|
||||||
|
|
||||||
context.define(currentUser, assumedRoles);
|
context.define(currentUser, assumedRoles);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType;
|
import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType;
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||||
|
@ -14,7 +14,6 @@ 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.Valid;
|
|
||||||
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;
|
||||||
@ -57,7 +56,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
|||||||
public ResponseEntity<HsOfficeSepaMandateResource> addSepaMandate(
|
public ResponseEntity<HsOfficeSepaMandateResource> addSepaMandate(
|
||||||
final String currentUser,
|
final String currentUser,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
@Valid final HsOfficeSepaMandateInsertResource body) {
|
final HsOfficeSepaMandateInsertResource body) {
|
||||||
|
|
||||||
context.define(currentUser, assumedRoles);
|
context.define(currentUser, assumedRoles);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType;
|
import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType;
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import org.postgresql.util.PGtokenizer;
|
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class PostgresArray {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a byte[], as returned for a Postgres-array by native queries, to a Java array.
|
|
||||||
*
|
|
||||||
* <p>This example code worked with Hibernate 5 (Spring Boot 3.0.x):
|
|
||||||
* <pre><code>
|
|
||||||
* return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
|
|
||||||
* </code></pre>
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>With Hibernate 6 (Spring Boot 3.1.x), this utility method can be used like such:
|
|
||||||
* <pre><code>
|
|
||||||
* final byte[] result = (byte[]) em.createNativeQuery("select * from currentSubjectsUuids() as uuids", UUID[].class)
|
|
||||||
* .getSingleResult();
|
|
||||||
* return fromPostgresArray(result, UUID.class, UUID::fromString);
|
|
||||||
* </code></pre>
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param pgArray the byte[] returned by a native query containing as rendered for a Postgres array
|
|
||||||
* @param elementClass the class of a single element of the Java array to be returned
|
|
||||||
* @param itemParser converts a string element to the specified elementClass
|
|
||||||
* @return a Java array containing the data from pgArray
|
|
||||||
* @param <T> type of a single element of the Java array
|
|
||||||
*/
|
|
||||||
public static <T> T[] fromPostgresArray(final byte[] pgArray, final Class<T> elementClass, final Function<String, T> itemParser) {
|
|
||||||
final var pgArrayLiteral = new String(pgArray, StandardCharsets.UTF_8);
|
|
||||||
if (pgArrayLiteral.length() == 2) {
|
|
||||||
return newGenericArray(elementClass, 0);
|
|
||||||
}
|
|
||||||
final PGtokenizer tokenizer = new PGtokenizer(pgArrayLiteral.substring(1, pgArrayLiteral.length()-1), ',');
|
|
||||||
tokenizer.remove("\"", "\"");
|
|
||||||
final T[] array = newGenericArray(elementClass, tokenizer.getSize()); // Create a new array of the specified type and length
|
|
||||||
for ( int n = 0; n < tokenizer.getSize(); ++n ) {
|
|
||||||
final String token = tokenizer.getToken(n);
|
|
||||||
if ( !"NULL".equals(token) ) {
|
|
||||||
array[n] = itemParser.apply(token.trim().replace("\\\"", "\""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T[] newGenericArray(final Class<T> elementClass, final int length) {
|
|
||||||
return (T[]) Array.newInstance(elementClass, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
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.HsOfficeReasonForTerminationResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeReasonForTerminationResource;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import com.vladmihalcea.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.HsOfficePartnerEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import com.vladmihalcea.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.HsOfficePartnerRepository;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.range.Range;
|
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.HsOfficeSepaMandatePatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource;
|
||||||
import net.hostsharing.test.PatchUnitTestBase;
|
import net.hostsharing.test.PatchUnitTestBase;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
||||||
|
|
||||||
import com.vladmihalcea.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.bankaccount.HsOfficeBankAccountRepository;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
||||||
|
@ -7,7 +7,6 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
|||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@ -30,9 +29,7 @@ class PostgresArrayIntegrationTest {
|
|||||||
return emptyArray;
|
return emptyArray;
|
||||||
end; $$;
|
end; $$;
|
||||||
""").executeUpdate();
|
""").executeUpdate();
|
||||||
final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnEmptyArray()", String[].class).getSingleResult();
|
final String[] result = (String[]) em.createNativeQuery("SELECT returnEmptyArray()", String[].class).getSingleResult();
|
||||||
|
|
||||||
final String[] result = PostgresArray.fromPostgresArray(pgArray, String.class, Function.identity());
|
|
||||||
|
|
||||||
assertThat(result).isEmpty();
|
assertThat(result).isEmpty();
|
||||||
}
|
}
|
||||||
@ -53,9 +50,7 @@ class PostgresArrayIntegrationTest {
|
|||||||
return array[text1, text2, text3, null, text4];
|
return array[text1, text2, text3, null, text4];
|
||||||
end; $$;
|
end; $$;
|
||||||
""").executeUpdate();
|
""").executeUpdate();
|
||||||
final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnStringArray()", String[].class).getSingleResult();
|
final String[] result = (String[]) em.createNativeQuery("SELECT returnStringArray()", String[].class).getSingleResult();
|
||||||
|
|
||||||
final String[] result = PostgresArray.fromPostgresArray(pgArray, String.class, Function.identity());
|
|
||||||
|
|
||||||
assertThat(result).containsExactly("one", "two, three", "four; five", null, "say \"Hello\" to me");
|
assertThat(result).containsExactly("one", "two, three", "four; five", null, "say \"Hello\" to me");
|
||||||
}
|
}
|
||||||
@ -75,9 +70,7 @@ class PostgresArrayIntegrationTest {
|
|||||||
return ARRAY[uuid1, uuid2, null, uuid3];
|
return ARRAY[uuid1, uuid2, null, uuid3];
|
||||||
end; $$;
|
end; $$;
|
||||||
""").executeUpdate();
|
""").executeUpdate();
|
||||||
final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnUuidArray()", UUID[].class).getSingleResult();
|
final UUID[] result = (UUID[]) em.createNativeQuery("SELECT returnUuidArray()", UUID[].class).getSingleResult();
|
||||||
|
|
||||||
final UUID[] result = PostgresArray.fromPostgresArray(pgArray, UUID.class, UUID::fromString);
|
|
||||||
|
|
||||||
assertThat(result).containsExactly(
|
assertThat(result).containsExactly(
|
||||||
UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479"),
|
UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479"),
|
||||||
|
Loading…
Reference in New Issue
Block a user