diff --git a/.aliases b/.aliases index ae9ceaf0..9477474d 100644 --- a/.aliases +++ b/.aliases @@ -44,7 +44,7 @@ alias podman-stop='systemctl --user disable --now podman.socket && systemctl --u alias podman-use='export DOCKER_HOST="unix:///run/user/$UID/podman/podman.sock"; export TESTCONTAINERS_RYUK_DISABLED=true' alias gw=gradleWrapper -alias pg-sql-run='docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:13.7-bullseye' +alias pg-sql-run='docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:15.5-bookworm' alias pg-sql-stop='docker stop hsadmin-ng-postgres' alias pg-sql-start='docker container start hsadmin-ng-postgres' alias pg-sql-remove='docker rm hsadmin-ng-postgres' diff --git a/README.md b/README.md index 73c9e84d..87860527 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ For architecture consider the files in the `doc` and `adr` folder. - [Directory and Package Structure](#directory-and-package-structure) - [General Directory Structure](#general-directory-structure) - [Source Code Package Structure](#source-code-package-structure) + - [Run Tests from Command Line](#run-tests-from-command-line) - [Spotless Code Formatting](#spotless-code-formatting) - [JaCoCo Test Code Coverage Check](#jacoco-test-code-coverage-check) - [PiTest Mutation Testing](#pitest-mutation-testing) @@ -39,6 +40,7 @@ For architecture consider the files in the `doc` and `adr` folder. - [How to Use a Persistent Database for Integration Tests?](#how-to-use-a-persistent-database-for-integration-tests?) - [How to Amend Liquibase SQL Changesets?](#how-to-amend-liquibase-sql-changesets?) - [How to Re-Generate Spring-Controller-Interfaces from OpenAPI specs?](#how-to-re-generate-spring-controller-interfaces-from-openapi-specs?) + - [How to Generate Database Table Diagrams?](#how-to-generate-database-table-diagrams?) - [Further Documentation](#further-documentation) @@ -49,25 +51,28 @@ Everything is tested on _Ubuntu Linux 22.04_ and _MacOS Monterey (12.4)_. To be able to build and run the Java Spring Boot application, you need the following tools: -- Docker 20.x (on MacOS you also need *Docker Desktop* or similar) -- PostgreSQL Server 13.7-bullseye +- Docker 20.x (on MacOS you also need *Docker Desktop* or similar) or Podman +- PostgreSQL Server 15.5-bookworm (see instructions below to install and run in Docker) - Java JDK at least recent enough to run Gradle (JDK 17.x will be automatically installed by Gradle toolchain support) -- Gradle in some not too outdated version (7.4 will be installed via wrapper) -You also might need an IDE (e.g. *IntelliJ IDEA* or *Eclipse* or *VS Code* with *[STS](https://spring.io/tools)* and a GUI Frontend for *PostgreSQL* like *Postbird*. +We recommend to use an IDE (e.g. *IntelliJ IDEA* or *Eclipse* or *VS Code* with *[STS](https://spring.io/tools)* and optionally a GUI Frontend for *PostgreSQL* like *Postbird*. -If you have at least Docker, the Java JDK and Gradle installed in appropriate versions and in your `PATH`, then you can start like this: +If you have at least Docker and the Java JDK installed in appropriate versions and in your `PATH`, then you can start like this: cd your-hsadmin-ng-directory - gradle wrapper # downloads the configured Gradle version into the project - source .aliases # creates some comforable bash aliases, e.g. 'gw'='./gradlew' + source .aliases # creates some comfortable bash aliases, e.g. 'gw'='./gradlew' + gw # initially downloads the configured Gradle version into the project gw test # compiles and runs unit- and integration-tests + # if the container has not been built yet, run this: pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 + # if the container has been built already, run this: + pg-sql-start + gw bootRun # compiles and runs the application on localhost:8080 # the following command should reply with "pong": @@ -130,14 +135,14 @@ But the easiest way to run PostgreSQL is via Docker. Initially, pull an image compatible to current PostgreSQL version of Hostsharing: - docker pull postgres:13.7-bullseye + docker pull postgres:15.5-bookworm **⚠** If we switch the version, please also amend the documentation as well as the aliases file. Thanks! Create and run a container with the given PostgreSQL version: - docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:13.7-bullseye + docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:15.5-bookworm # or via alias: pg-sql-run @@ -196,7 +201,7 @@ To generate the TOC (Table of Contents), a little bash script from a Given this is in PATH as `md-toc`, use: ```shell -md-toc ^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$ CVE-2022-42003 + + + ^pkg:maven/org\.eclipse\.angus/angus\-activation@.*$ + cpe:/a:eclipse:eclipse_ide + + + + ^pkg:maven/jakarta\.activation/jakarta\.activation\-api@.*$ + cpe:/a:eclipse:eclipse_ide + + + + ^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$ + cpe:/a:fasterxml:jackson-databind + + + + ^pkg:maven/com\.jayway\.jsonpath/json\-path@.*$ + CVE-2023-51074 + + + + ^pkg:maven/org\.pitest/pitest\-command\-line@.*$ + cpe:/a:line:line + + + + ^pkg:maven/org\.yaml/snakeyaml@.*$ + CVE-2022-1471 + diff --git a/gradle.properties b/gradle.properties index a033d0d3..433cede1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,8 @@ +# Gradle Java Toolchain-support +org.gradle.java.installations.auto-detect=true +org.gradle.java.installations.auto-download=true +# org.gradle.jvm.toolchain.install.adoptopenjdk.baseUri +# org.gradle.java.installations.paths -- uncomment and set if needed # Spring BOM overrides # currently none necessary diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a..d64cd491 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8049c684..1af9e093 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle index 8c454c71..09d09d6f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,4 +7,32 @@ pluginManagement { } } +plugins { + 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' diff --git a/src/main/java/net/hostsharing/hsadminng/config/PostgresCustomDialect.java b/src/main/java/net/hostsharing/hsadminng/config/PostgresCustomDialect.java index 9cd2ec70..3c66716d 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/PostgresCustomDialect.java +++ b/src/main/java/net/hostsharing/hsadminng/config/PostgresCustomDialect.java @@ -8,7 +8,7 @@ import static org.hibernate.dialect.DatabaseVersion.make; public class PostgresCustomDialect extends PostgreSQLDialect { public PostgresCustomDialect() { - super(make(13, 7)); + super(make(15, 5)); } } diff --git a/src/main/java/net/hostsharing/hsadminng/context/Context.java b/src/main/java/net/hostsharing/hsadminng/context/Context.java index f7f6f827..2730147d 100644 --- a/src/main/java/net/hostsharing/hsadminng/context/Context.java +++ b/src/main/java/net/hostsharing/hsadminng/context/Context.java @@ -15,9 +15,11 @@ import java.util.Collections; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static java.util.function.Predicate.not; +import static net.hostsharing.hsadminng.mapper.PostgresArray.fromPostgresArray; import static org.springframework.transaction.annotation.Propagation.MANDATORY; @Service @@ -81,11 +83,14 @@ public class Context { } public String[] getAssumedRoles() { - return (String[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult(); + final byte[] result = (byte[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult(); + return fromPostgresArray(result, String.class, Function.identity()); } public UUID[] currentSubjectsUuids() { - return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult(); + final byte[] result = (byte[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class) + .getSingleResult(); + return fromPostgresArray(result, UUID.class, UUID::fromString); } public static String getCallerMethodNameFromStackFrame(final int skipFrames) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepository.java index a39acbfa..309c3a57 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepository.java @@ -14,7 +14,7 @@ public interface HsOfficeContactRepository extends Repository findContactByOptionalLabelLike(String label); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index 9955f6f1..e699fb5c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -1,13 +1,11 @@ package net.hostsharing.hsadminng.hs.office.coopassets; -import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Type; import jakarta.persistence.*; import java.math.BigDecimal; @@ -47,7 +45,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable { @Column(name = "transactiontype") @Enumerated(EnumType.STRING) - @Type(PostgreSQLEnumType.class) private HsOfficeCoopAssetsTransactionType transactionType; @Column(name = "valuedate") diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java index 1a14abde..256933b9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.office.coopassets; -import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionEntity; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index 1b5d1cc5..b5d4979b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -1,12 +1,10 @@ package net.hostsharing.hsadminng.hs.office.coopshares; -import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; -import org.hibernate.annotations.Type; import jakarta.persistence.*; import java.time.LocalDate; @@ -43,7 +41,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable { @Column(name = "transactiontype") @Enumerated(EnumType.STRING) - @Type(PostgreSQLEnumType.class) private HsOfficeCoopSharesTransactionType transactionType; @Column(name = "valuedate") diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java index 27cb6f92..f0013ef9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java @@ -19,15 +19,15 @@ public interface HsOfficeDebitorRepository extends Repository findDebitorByOptionalNameLike(String name); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java index 7a3e1a20..671ae7f7 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.office.membership; -import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType; import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType; import com.vladmihalcea.hibernate.type.range.Range; import lombok.*; @@ -61,7 +60,6 @@ public class HsOfficeMembershipEntity implements Stringifyable { @Column(name = "reasonfortermination") @Enumerated(EnumType.STRING) - @Type(PostgreSQLEnumType.class) private HsOfficeReasonForTermination reasonForTermination; public void setValidFrom(final LocalDate validFrom) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java index 222dcaed..6c7a158c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java @@ -13,14 +13,14 @@ public interface HsOfficePartnerRepository extends Repository findPartnerByOptionalNameLike(String name); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java index cdc695f0..a76d4130 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java @@ -1,13 +1,11 @@ package net.hostsharing.hsadminng.hs.office.person; -import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.apache.commons.lang3.StringUtils; -import org.hibernate.annotations.Type; import jakarta.persistence.*; import java.util.UUID; @@ -37,7 +35,6 @@ public class HsOfficePersonEntity implements Stringifyable { @Column(name = "persontype") @Enumerated(EnumType.STRING) - @Type(PostgreSQLEnumType.class) private HsOfficePersonType personType; @Column(name = "tradename") diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepository.java index 538ffaf1..f7481339 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepository.java @@ -14,9 +14,9 @@ public interface HsOfficePersonRepository extends Repository findPersonByOptionalNameLike(String name); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java index 3d6332e3..98c6bccf 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java @@ -51,7 +51,7 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi final HsOfficeRelationshipTypeResource relationshipType) { context.define(currentUser, assumedRoles); - final var entities = relationshipRepo.findRelationshipRelatedToPersonUuid(personUuid, + final var entities = relationshipRepo.findRelationshipRelatedToPersonUuidAndRelationshipType(personUuid, mapper.map(relationshipType, HsOfficeRelationshipType.class)); final var resources = mapper.mapList(entities, HsOfficeRelationshipResource.class, diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java index 9e7fb5d9..383c6853 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java @@ -1,12 +1,10 @@ package net.hostsharing.hsadminng.hs.office.relationship; -import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.stringify.Stringify; -import org.hibernate.annotations.Type; import jakarta.persistence.*; import java.util.UUID; @@ -47,7 +45,6 @@ public class HsOfficeRelationshipEntity { @Column(name = "reltype") @Enumerated(EnumType.STRING) - @Type(PostgreSQLEnumType.class) private HsOfficeRelationshipType relType; @Override diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java index 8b9e10fe..d34caa8c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java @@ -12,8 +12,8 @@ public interface HsOfficeRelationshipRepository extends Repository findByUuid(UUID id); - default List findRelationshipRelatedToPersonUuid(@NotNull UUID personUuid, HsOfficeRelationshipType relationshipType) { - return findRelationshipRelatedToPersonUuid(personUuid, relationshipType.toString()); + default List findRelationshipRelatedToPersonUuidAndRelationshipType(@NotNull UUID personUuid, HsOfficeRelationshipType relationshipType) { + return findRelationshipRelatedToPersonUuidAndRelationshipTypeString(personUuid, relationshipType.toString()); } @Query(value = """ @@ -27,7 +27,7 @@ public interface HsOfficeRelationshipRepository extends Repository findRelationshipRelatedToPersonUuid(@NotNull UUID personUuid, String relationshipType); + List findRelationshipRelatedToPersonUuidAndRelationshipTypeString(@NotNull UUID personUuid, String relationshipType); HsOfficeRelationshipEntity save(final HsOfficeRelationshipEntity entity); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index db31adcb..84264bd6 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -54,6 +54,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable { @Column(name = "validity", columnDefinition = "daterange") @Type(PostgreSQLRangeType.class) + @Builder.Default private Range validity = Range.infinite(LocalDate.class); public void setValidFrom(final LocalDate validFrom) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepository.java index d243a716..aab53bae 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepository.java @@ -14,7 +14,7 @@ public interface HsOfficeSepaMandateRepository extends Repository findSepaMandateByOptionalIban(String iban); diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java b/src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java new file mode 100644 index 00000000..e1e1d056 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java @@ -0,0 +1,58 @@ +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. + * + *

This example code worked with Hibernate 5 (Spring Boot 3.0.x): + *


+     *      return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
+     * 
+ *

+ * + *

With Hibernate 6 (Spring Boot 3.1.x), this utility method can be used like such: + *


+     *      final byte[] result = (byte[]) em.createNativeQuery("select * from currentSubjectsUuids() as uuids", UUID[].class)
+     *                 .getSingleResult();
+     *      return fromPostgresArray(result, UUID.class, UUID::fromString);
+     * 
+ *

+ * + * @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 type of a single element of the Java array + */ + public static T[] fromPostgresArray(final byte[] pgArray, final Class elementClass, final Function 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[] newGenericArray(final Class elementClass, final int length) { + return (T[]) Array.newInstance(elementClass, length); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepository.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepository.java index bfe11a19..0c1a168b 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepository.java @@ -11,7 +11,7 @@ public interface RbacUserRepository extends Repository { @Query(""" select u from RbacUserEntity u - where :userName is null or u.name like concat(:userName, '%') + where :userName is null or u.name like concat(cast(:userName as text), '%') order by u.name """) List findByOptionalNameLike(String userName); diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java index 530a7006..1bd000ba 100644 --- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java @@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import java.util.List; -import java.util.UUID; @RestController public class TestCustomerController implements TestCustomersApi { diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepository.java index a882b304..2dc298ea 100644 --- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepository.java @@ -12,7 +12,7 @@ public interface TestCustomerRepository extends Repository findByUuid(UUID id); - @Query("SELECT c FROM TestCustomerEntity c WHERE :prefix is null or c.prefix like concat(:prefix, '%')") + @Query("SELECT c FROM TestCustomerEntity c WHERE :prefix is null or c.prefix like concat(cast(:prefix as text), '%')") List findCustomerByOptionalPrefixLike(String prefix); TestCustomerEntity save(final TestCustomerEntity entity); diff --git a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageRepository.java b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageRepository.java index 610d8fdc..f8538465 100644 --- a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageRepository.java @@ -8,7 +8,7 @@ import java.util.UUID; public interface TestPackageRepository extends Repository { - @Query("SELECT p FROM TestPackageEntity p WHERE :name is null or p.name like concat(:name, '%')") + @Query("SELECT p FROM TestPackageEntity p WHERE :name is null or p.name like concat(cast(:name as text), '%')") List findAllByOptionalNameLike(final String name); TestPackageEntity findByUuid(UUID packageUuid); diff --git a/src/main/resources/db/changelog/206-hs-office-contact-migration.sql b/src/main/resources/db/changelog/206-hs-office-contact-migration.sql new file mode 100644 index 00000000..79cdd3bf --- /dev/null +++ b/src/main/resources/db/changelog/206-hs-office-contact-migration.sql @@ -0,0 +1,96 @@ +--liquibase formatted sql + +-- TODO: These changesets are just for the external remote views to simulate the legacy tables. +-- Once we don't need the external remote views anymore, create revert changesets. + +-- ============================================================================ +--changeset hs-office-contact-MIGRATION-mapping:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE TABLE hs_office_contact_legacy_id +( + uuid uuid NOT NULL REFERENCES hs_office_contact(uuid), + contact_id integer NOT NULL +); +--// + + +-- ============================================================================ +--changeset hs-office-contact-MIGRATION-sequence:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE SEQUENCE IF NOT EXISTS hs_office_contact_legacy_id_seq + AS integer + START 1000000000 + OWNED BY hs_office_contact_legacy_id.contact_id; +--// + + +-- ============================================================================ +--changeset hs-office-contact-MIGRATION-default:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +ALTER TABLE hs_office_contact_legacy_id + ALTER COLUMN contact_id + SET DEFAULT nextVal('hs_office_contact_legacy_id_seq'); + +--/ + +-- ============================================================================ +--changeset hs-office-contact-MIGRATION-insert:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CALL defineContext('schema-migration'); +INSERT INTO hs_office_contact_legacy_id(uuid, contact_id) + SELECT uuid, nextVal('hs_office_contact_legacy_id_seq') FROM hs_office_contact; +--/ + + +-- ============================================================================ +--changeset hs-office-contact-MIGRATION-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function insertContactLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of trigger'; + end if; + + INSERT INTO hs_office_contact_legacy_id VALUES + (NEW.uuid, nextVal('hs_office_contact_legacy_id_seq')); + + return NEW; +end; $$; + +create trigger createContactLegacyIdMapping + after insert on hs_office_contact + for each row + execute procedure insertContactLegacyIdMapping(); +--/ + + +-- ============================================================================ +--changeset hs-office-contact-MIGRATION-delete-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function deleteContactLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'DELETE' then + raise exception 'invalid usage of trigger'; + end if; + + DELETE FROM hs_office_contact_legacy_id + WHERE uuid = OLD.uuid; + + return OLD; +end; $$; + +create trigger removeContactLegacyIdMapping + before delete on hs_office_contact + for each row + execute procedure deleteContactLegacyIdMapping(); +--/ diff --git a/src/main/resources/db/changelog/226-hs-office-partner-migration.sql b/src/main/resources/db/changelog/226-hs-office-partner-migration.sql new file mode 100644 index 00000000..f48e99d5 --- /dev/null +++ b/src/main/resources/db/changelog/226-hs-office-partner-migration.sql @@ -0,0 +1,95 @@ +--liquibase formatted sql + +-- TODO: These changesets are just for the external remote views to simulate the legacy tables. +-- Once we don't need the external remote views anymore, create revert changesets. + +-- ============================================================================ +--changeset hs-office-partner-MIGRATION-mapping:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE TABLE hs_office_partner_legacy_id +( + uuid uuid NOT NULL REFERENCES hs_office_partner(uuid), + bp_id integer NOT NULL +); +--// + + +-- ============================================================================ +--changeset hs-office-partner-MIGRATION-sequence:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE SEQUENCE IF NOT EXISTS hs_office_partner_legacy_id_seq + AS integer + START 1000000000 + OWNED BY hs_office_partner_legacy_id.bp_id; +--// + + +-- ============================================================================ +--changeset hs-office-partner-MIGRATION-default:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +ALTER TABLE hs_office_partner_legacy_id + ALTER COLUMN bp_id + SET DEFAULT nextVal('hs_office_partner_legacy_id_seq'); +--/ + +-- ============================================================================ +--changeset hs-office-partner-MIGRATION-insert:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CALL defineContext('schema-migration'); +INSERT INTO hs_office_partner_legacy_id(uuid, bp_id) + SELECT uuid, nextVal('hs_office_partner_legacy_id_seq') FROM hs_office_partner; +--/ + + +-- ============================================================================ +--changeset hs-office-partner-MIGRATION-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function insertPartnerLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of trigger'; + end if; + + INSERT INTO hs_office_partner_legacy_id VALUES + (NEW.uuid, nextVal('hs_office_partner_legacy_id_seq')); + + return NEW; +end; $$; + +create trigger createPartnerLegacyIdMapping + after insert on hs_office_partner + for each row + execute procedure insertPartnerLegacyIdMapping(); +--/ + + +-- ============================================================================ +--changeset hs-office-partner-MIGRATION-delete-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function deletePartnerLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'DELETE' then + raise exception 'invalid usage of trigger'; + end if; + + DELETE FROM hs_office_partner_legacy_id + WHERE uuid = OLD.uuid; + + return OLD; +end; $$; + +create trigger removePartnerLegacyIdMapping + before delete on hs_office_partner + for each row + execute procedure deletePartnerLegacyIdMapping(); +--/ diff --git a/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql b/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql new file mode 100644 index 00000000..fe43706c --- /dev/null +++ b/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql @@ -0,0 +1,97 @@ +--liquibase formatted sql + +-- TODO: These changesets are just for the external remote views to simulate the legacy tables. +-- Once we don't need the external remote views anymore, create revert changesets. + +-- ============================================================================ +--changeset hs-office-sepamandate-MIGRATION-mapping:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE TABLE hs_office_sepamandate_legacy_id +( + uuid uuid NOT NULL REFERENCES hs_office_sepamandate(uuid), + sepa_mandat_id integer NOT NULL +); +--// + + +-- ============================================================================ +--changeset hs-office-sepamandate-MIGRATION-sequence:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE SEQUENCE IF NOT EXISTS hs_office_sepamandate_legacy_id_seq + AS integer + START 1000000000 + OWNED BY hs_office_sepamandate_legacy_id.sepa_mandat_id; +--// + + +-- ============================================================================ +--changeset hs-office-sepamandate-MIGRATION-default:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +ALTER TABLE hs_office_sepamandate_legacy_id + ALTER COLUMN sepa_mandat_id + SET DEFAULT nextVal('hs_office_sepamandate_legacy_id_seq'); + +--/ + + +-- ============================================================================ +--changeset hs-office-sepamandate-MIGRATION-insert:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CALL defineContext('schema-migration'); +INSERT INTO hs_office_sepamandate_legacy_id(uuid, sepa_mandat_id) + SELECT uuid, nextVal('hs_office_sepamandate_legacy_id_seq') FROM hs_office_sepamandate; +--/ + + +-- ============================================================================ +--changeset hs-office-sepamandate-MIGRATION-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function insertSepaMandateLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of trigger'; + end if; + + INSERT INTO hs_office_sepamandate_legacy_id VALUES + (NEW.uuid, nextVal('hs_office_sepamandate_legacy_id_seq')); + + return NEW; +end; $$; + +create trigger createSepaMandateLegacyIdMapping + after insert on hs_office_sepamandate + for each row + execute procedure insertSepaMandateLegacyIdMapping(); +--/ + + +-- ============================================================================ +--changeset hs-office-sepamandate-MIGRATION-delete-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function deleteSepaMandateLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'DELETE' then + raise exception 'invalid usage of trigger'; + end if; + + DELETE FROM hs_office_sepamandate_legacy_id + WHERE uuid = OLD.uuid; + + return OLD; +end; $$; + +create trigger removeSepaMandateLegacyIdMapping + before delete on hs_office_sepamandate + for each row + execute procedure deleteSepaMandateLegacyIdMapping(); +--/ diff --git a/src/main/resources/db/changelog/316-hs-office-coopshares-migration.sql b/src/main/resources/db/changelog/316-hs-office-coopshares-migration.sql new file mode 100644 index 00000000..dd64356e --- /dev/null +++ b/src/main/resources/db/changelog/316-hs-office-coopshares-migration.sql @@ -0,0 +1,96 @@ +--liquibase formatted sql + +-- TODO: These changesets are just for the external remote views to simulate the legacy tables. +-- Once we don't need the external remote views anymore, create revert changesets. + +-- ============================================================================ +--changeset hs-office-coopshares-MIGRATION-mapping:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE TABLE hs_office_coopsharestransaction_legacy_id +( + uuid uuid NOT NULL REFERENCES hs_office_coopsharestransaction(uuid), + member_share_id integer NOT NULL +); +--// + + +-- ============================================================================ +--changeset hs-office-coopshares-MIGRATION-sequence:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE SEQUENCE IF NOT EXISTS hs_office_coopsharestransaction_legacy_id_seq + AS integer + START 1000000000 + OWNED BY hs_office_coopsharestransaction_legacy_id.member_share_id; +--// + + +-- ============================================================================ +--changeset hs-office-coopshares-MIGRATION-default:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +ALTER TABLE hs_office_coopsharestransaction_legacy_id + ALTER COLUMN member_share_id + SET DEFAULT nextVal('hs_office_coopsharestransaction_legacy_id_seq'); + +--/ + +-- ============================================================================ +--changeset hs-office-coopshares-MIGRATION-insert:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CALL defineContext('schema-migration'); +INSERT INTO hs_office_coopsharestransaction_legacy_id(uuid, member_share_id) + SELECT uuid, nextVal('hs_office_coopsharestransaction_legacy_id_seq') FROM hs_office_coopsharestransaction; +--/ + + +-- ============================================================================ +--changeset hs-office-coopShares-MIGRATION-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function insertCoopSharesLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of trigger'; + end if; + + INSERT INTO hs_office_coopsharestransaction_legacy_id VALUES + (NEW.uuid, nextVal('hs_office_coopsharestransaction_legacy_id_seq')); + + return NEW; +end; $$; + +create trigger createCoopSharesLegacyIdMapping + after insert on hs_office_coopsharestransaction + for each row + execute procedure insertCoopSharesLegacyIdMapping(); +--/ + + +-- ============================================================================ +--changeset hs-office-coopShares-MIGRATION-delete-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function deleteCoopSharesLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'DELETE' then + raise exception 'invalid usage of trigger'; + end if; + + DELETE FROM hs_office_coopsharestransaction_legacy_id + WHERE uuid = OLD.uuid; + + return OLD; +end; $$; + +create trigger removeCoopSharesLegacyIdMapping + before delete on hs_office_coopsharestransaction + for each row + execute procedure deleteCoopSharesLegacyIdMapping(); +--/ diff --git a/src/main/resources/db/changelog/326-hs-office-coopassets-migration.sql b/src/main/resources/db/changelog/326-hs-office-coopassets-migration.sql new file mode 100644 index 00000000..8c346566 --- /dev/null +++ b/src/main/resources/db/changelog/326-hs-office-coopassets-migration.sql @@ -0,0 +1,96 @@ +--liquibase formatted sql + +-- TODO: These changesets are just for the external remote views to simulate the legacy tables. +-- Once we don't need the external remote views anymore, create revert changesets. + +-- ============================================================================ +--changeset hs-office-coopassets-MIGRATION-mapping:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE TABLE hs_office_coopassetstransaction_legacy_id +( + uuid uuid NOT NULL REFERENCES hs_office_coopassetstransaction(uuid), + member_asset_id integer NOT NULL +); +--// + + +-- ============================================================================ +--changeset hs-office-coopassets-MIGRATION-sequence:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE SEQUENCE IF NOT EXISTS hs_office_coopassetstransaction_legacy_id_seq + AS integer + START 1000000000 + OWNED BY hs_office_coopassetstransaction_legacy_id.member_asset_id; +--// + + +-- ============================================================================ +--changeset hs-office-coopassets-MIGRATION-default:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +ALTER TABLE hs_office_coopassetstransaction_legacy_id + ALTER COLUMN member_asset_id + SET DEFAULT nextVal('hs_office_coopassetstransaction_legacy_id_seq'); +--/ + + +-- ============================================================================ +--changeset hs-office-coopassets-MIGRATION-insert:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CALL defineContext('schema-migration'); +INSERT INTO hs_office_coopassetstransaction_legacy_id(uuid, member_asset_id) + SELECT uuid, nextVal('hs_office_coopassetstransaction_legacy_id_seq') FROM hs_office_coopassetstransaction; +--/ + + +-- ============================================================================ +--changeset hs-office-coopAssets-MIGRATION-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function insertCoopAssetsLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of trigger'; + end if; + + INSERT INTO hs_office_coopassetstransaction_legacy_id VALUES + (NEW.uuid, nextVal('hs_office_coopassetstransaction_legacy_id_seq')); + + return NEW; +end; $$; + +create trigger createCoopAssetsLegacyIdMapping + after insert on hs_office_coopassetstransaction + for each row + execute procedure insertCoopAssetsLegacyIdMapping(); +--/ + + +-- ============================================================================ +--changeset hs-office-coopAssets-MIGRATION-delete-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +create or replace function deleteCoopAssetsLegacyIdMapping() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP <> 'DELETE' then + raise exception 'invalid usage of trigger'; + end if; + + DELETE FROM hs_office_coopassetstransaction_legacy_id + WHERE uuid = OLD.uuid; + + return OLD; +end; $$; + +create trigger removeCoopAssetsLegacyIdMapping + before delete on hs_office_coopassetstransaction + for each row + execute procedure deleteCoopAssetsLegacyIdMapping(); +--/ diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 3a1bb533..68719b66 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -53,6 +53,8 @@ databaseChangeLog: file: db/changelog/200-hs-office-contact.sql - include: file: db/changelog/203-hs-office-contact-rbac.sql + - include: + file: db/changelog/206-hs-office-contact-migration.sql - include: file: db/changelog/208-hs-office-contact-test-data.sql - include: @@ -67,6 +69,8 @@ databaseChangeLog: file: db/changelog/223-hs-office-partner-rbac.sql - include: file: db/changelog/224-hs-office-partner-details-rbac.sql + - include: + file: db/changelog/226-hs-office-partner-migration.sql - include: file: db/changelog/228-hs-office-partner-test-data.sql - include: @@ -80,7 +84,7 @@ databaseChangeLog: - include: file: db/changelog/243-hs-office-bankaccount-rbac.sql - include: - file: db/changelog/248-hs-office-bankaccount-test-data.sql + file: db/changelog/248-hs-office-bankaccount-test-data.sql - include: file: db/changelog/270-hs-office-debitor.sql - include: @@ -91,6 +95,8 @@ databaseChangeLog: file: db/changelog/250-hs-office-sepamandate.sql - include: file: db/changelog/253-hs-office-sepamandate-rbac.sql + - include: + file: db/changelog/256-hs-office-sepamandate-migration.sql - include: file: db/changelog/258-hs-office-sepamandate-test-data.sql - include: @@ -103,11 +109,15 @@ databaseChangeLog: file: db/changelog/310-hs-office-coopshares.sql - include: file: db/changelog/313-hs-office-coopshares-rbac.sql + - include: + file: db/changelog/316-hs-office-coopshares-migration.sql - include: file: db/changelog/318-hs-office-coopshares-test-data.sql - include: file: db/changelog/320-hs-office-coopassets.sql - include: file: db/changelog/323-hs-office-coopassets-rbac.sql + - include: + file: db/changelog/326-hs-office-coopassets-migration.sql - include: file: db/changelog/328-hs-office-coopassets-test-data.sql diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java index a54ca5c6..d870ca1a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java @@ -83,7 +83,7 @@ class HsOfficeBankAccountControllerRestTest { enum InvalidBicTestCase { TOO_SHORT("BEVODEB", "Bic length must be 8 or 11"), TOO_LONG("BEVODEBBX", "Bic length must be 8 or 11"), - INVALID_CHARACTER("BEV-ODEB", "Bank code must contain only letters."); + INVALID_CHARACTER("BEV-ODEB", "Bank code must contain only alphanumeric."); private final String givenBic; private final String expectedErrorMessage; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/TestHsOfficeBankAccount.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/TestHsOfficeBankAccount.java index 7bb7de7e..7b7505f4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/TestHsOfficeBankAccount.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/TestHsOfficeBankAccount.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.office.bankaccount; -import java.util.UUID; public class TestHsOfficeBankAccount { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java index a58aa824..0308c31d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java @@ -28,7 +28,6 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantD import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; @DataJpaTest @Import( { Context.class, JpaAttempt.class }) @@ -237,10 +236,6 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTest { final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll()); final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()); final var givenContact = givenSomeTemporaryContact("selfregistered-user-drew@hostsharing.org"); - assumeThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created") - .isEqualTo(initialRoleNames.size() + 3); - assumeThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created") - .isEqualTo(initialGrantNames.size() + 7); // when final var result = jpaAttempt.transacted(() -> { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/TestHsOfficeContact.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/TestHsOfficeContact.java index 58284258..b42ef8e5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/TestHsOfficeContact.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/TestHsOfficeContact.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.office.contact; -import java.util.UUID; public class TestHsOfficeContact { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index 22001cb2..76d6758f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -26,7 +26,6 @@ import java.util.UUID; import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; import static org.hamcrest.Matchers.*; @SpringBootTest( @@ -481,7 +480,7 @@ class HsOfficeDebitorControllerAcceptanceTest { void contactAdminUser_canNotDeleteRelatedDebitor() { context.define("superuser-alex@hostsharing.net"); final var givenDebitor = givenSomeTemporaryDebitor(); - assumeThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("forth contact"); + assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("forth contact"); RestAssured // @formatter:off .given() @@ -501,7 +500,7 @@ class HsOfficeDebitorControllerAcceptanceTest { void normalUser_canNotDeleteUnrelatedDebitor() { context.define("superuser-alex@hostsharing.net"); final var givenDebitor = givenSomeTemporaryDebitor(); - assumeThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("forth contact"); + assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("forth contact"); RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java index 9d2c6b7f..d9d482ba 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java @@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.office.debitor; import lombok.experimental.UtilityClass; -import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT; import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index 42f61495..c85a9b13 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -32,7 +32,6 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantD import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; @DataJpaTest @Import( { Context.class, JpaAttempt.class }) @@ -327,7 +326,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // when final var result = jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net", "hs_office_debitor#10003ThirdOHG-thirdcontact.admin"); - assumeThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent(); + assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent(); membershipRepo.deleteByUuid(givenMembership.getUuid()); }); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java index 1b40a4ce..d9245fc8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java @@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.office.membership; import com.vladmihalcea.hibernate.type.range.Range; import java.time.LocalDate; -import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java index 899b151d..053b03e1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java @@ -24,7 +24,6 @@ import java.util.UUID; import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; @@ -408,7 +407,7 @@ class HsOfficePartnerControllerAcceptanceTest { void contactAdminUser_canNotDeleteRelatedPartner() { context.define("superuser-alex@hostsharing.net"); final var givenPartner = givenSomeTemporaryPartnerBessler(); - assumeThat(givenPartner.getContact().getLabel()).isEqualTo("forth contact"); + assertThat(givenPartner.getContact().getLabel()).isEqualTo("forth contact"); RestAssured // @formatter:off .given() @@ -428,7 +427,7 @@ class HsOfficePartnerControllerAcceptanceTest { void normalUser_canNotDeleteUnrelatedPartner() { context.define("superuser-alex@hostsharing.net"); final var givenPartner = givenSomeTemporaryPartnerBessler(); - assumeThat(givenPartner.getContact().getLabel()).isEqualTo("forth contact"); + assertThat(givenPartner.getContact().getLabel()).isEqualTo("forth contact"); RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index 6b035f5b..e03d8cbe 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -29,7 +29,6 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantD import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; @DataJpaTest @Import( { Context.class, JpaAttempt.class }) @@ -330,7 +329,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { // when final var result = jpaAttempt.transacted(() -> { context("person-ErbenBesslerMelBessler@example.com"); - assumeThat(partnerRepo.findByUuid(givenPartner.getUuid())).isPresent(); + assertThat(partnerRepo.findByUuid(givenPartner.getUuid())).isPresent(); partnerRepo.deleteByUuid(givenPartner.getUuid()); }); @@ -352,10 +351,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); final var givenPartner = givenSomeTemporaryPartnerBessler("twelfth"); - assumeThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created") - .isEqualTo(initialRoleNames.length + 3); - assumeThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created") - .isEqualTo(initialGrantNames.length + 10); // when final var result = jpaAttempt.transacted(() -> { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java index 21756b6d..19235167 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java @@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.office.partner; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; -import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGAL; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java index 6c75434e..2405b237 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java @@ -27,7 +27,6 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantD import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; @DataJpaTest @Import( { Context.class, JpaAttempt.class }) @@ -244,10 +243,6 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTest { final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll()); final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()); final var givenPerson = givenSomeTemporaryPerson("selfregistered-user-drew@hostsharing.org"); - assumeThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created") - .isEqualTo(initialRoleNames.size() + 3); - assumeThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created") - .isEqualTo(initialGrantNames.size() + 7); // when final var result = jpaAttempt.transacted(() -> { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/TestHsOfficePerson.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/TestHsOfficePerson.java index f4d10fda..d394ee56 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/TestHsOfficePerson.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/TestHsOfficePerson.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.office.person; -import java.util.UUID; public class TestHsOfficePerson { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java index 6288bb4c..f090295b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java @@ -25,7 +25,6 @@ import java.util.UUID; import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityUnitTest.java index 7ba77e0e..5d8fa5b5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityUnitTest.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.office.sepamandate; -import com.vladmihalcea.hibernate.type.range.Range; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java index cbc8bfbc..8e5f5c79 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java @@ -31,7 +31,6 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantD import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @@ -346,7 +345,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTest { // when final var result = jpaAttempt.transacted(() -> { context("bankaccount-admin@ThirdOHG.example.com"); - assumeThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent(); + assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent(); sepaMandateRepo.deleteByUuid(givenSepaMandate.getUuid()); }); diff --git a/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java new file mode 100644 index 00000000..c76141b1 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java @@ -0,0 +1,88 @@ +package net.hostsharing.hsadminng.mapper; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import jakarta.persistence.EntityManager; + +import java.util.UUID; +import java.util.function.Function; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class PostgresArrayIntegrationTest { + + @Autowired + EntityManager em; + + @Test + void shouldCreateEmptyArray() { + em.createNativeQuery(""" + create or replace function returnEmptyArray() + returns text[] + stable leakproof + language plpgsql as $$ + declare + emptyArray text[] = '{}'; + begin + return emptyArray; + end; $$; + """).executeUpdate(); + final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnEmptyArray()", String[].class).getSingleResult(); + + final String[] result = PostgresArray.fromPostgresArray(pgArray, String.class, Function.identity()); + + assertThat(result).isEmpty(); + } + + @Test + void shouldCreateStringArray() { + em.createNativeQuery(""" + create or replace function returnStringArray() + returns varchar(63)[] + stable leakproof + language plpgsql as $$ + declare + text1 text = 'one'; + text2 text = 'two, three'; + text3 text = 'four; five'; + text4 text = 'say "Hello" to me'; + begin + return array[text1, text2, text3, null, text4]; + end; $$; + """).executeUpdate(); + final byte[] pgArray = (byte[]) 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"); + } + + @Test + void shouldCreateUUidArray() { + em.createNativeQuery(""" + create or replace function returnUuidArray() + returns uuid[] + stable leakproof + language plpgsql as $$ + declare + uuid1 UUID = 'f47ac10b-58cc-4372-a567-0e02b2c3d479'; + uuid2 UUID = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; + uuid3 UUID = '01234567-89ab-cdef-0123-456789abcdef'; + begin + return ARRAY[uuid1, uuid2, null, uuid3]; + end; $$; + """).executeUpdate(); + final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnUuidArray()", UUID[].class).getSingleResult(); + + final UUID[] result = PostgresArray.fromPostgresArray(pgArray, UUID.class, UUID::fromString); + + assertThat(result).containsExactly( + UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479"), + UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), + null, + UUID.fromString("01234567-89ab-cdef-0123-456789abcdef")); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java index 0402dbfe..6f0abc93 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.*; @@ -343,7 +342,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest { } private void assumeCreated(final ValidatableResponse response) { - assumeThat(response.extract().response().statusCode()).isEqualTo(201); + assertThat(response.extract().response().statusCode()).isEqualTo(201); } class Subject { @@ -479,7 +478,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest { } private void assumeGrantExists(final Subject grantingSubject, final String expectedGrant) { - assumeThat(findAllGrantsOf(grantingSubject)) + assertThat(findAllGrantsOf(grantingSubject)) .extracting(RbacGrantEntity::toDisplay) .contains(expectedGrant); } diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java index 3ff9eda0..3b09e861 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java @@ -25,7 +25,6 @@ import java.util.UUID; import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; @DataJpaTest @Import( { Context.class, JpaAttempt.class }) @@ -186,9 +185,8 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest { // when context("customer-admin@xxx.example.com", "test_customer#xxx.admin"); - final var revokeAttempt = attempt(em, () -> { - rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId()); - }); + final var revokeAttempt = attempt(em, () -> + rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId())); // then context("customer-admin@xxx.example.com", "test_customer#xxx.admin"); @@ -208,9 +206,8 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest { // when context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00.admin"); - final var revokeAttempt = attempt(em, () -> { - rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId()); - }); + final var revokeAttempt = attempt(em, () -> + rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId())); // then assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull(); @@ -230,9 +227,8 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest { // when context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00.admin"); - final var revokeAttempt = attempt(em, () -> { - rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId()); - }); + final var revokeAttempt = attempt(em, () -> + rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId())); // then revokeAttempt.assertExceptionWithRootCauseMessage( @@ -255,10 +251,10 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest { rbacGrantRepository.save(grant) ); - assumeThat(grantAttempt.caughtException()).isNull(); - assumeThat(rawRbacGrantRepository.findAll()) + assertThat(grantAttempt.caughtException()).isNull(); + assertThat(rawRbacGrantRepository.findAll()) .extracting(RawRbacGrantEntity::toDisplay) - .contains("{ grant role %s to user %s by role %s and assume }".formatted( + .contains("{ grant role %s to user %s by %s and assume }".formatted( with.grantedRole, with.granteeUserName, with.assumedRole )); diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java index bd2257ef..ea0a3109 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java @@ -61,11 +61,6 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest { assertThat(result.returnedValue()).isNotNull() .extracting(RbacUserEntity::getUuid).isEqualTo(givenUuid); assertThat(rbacUserRepository.findByName(result.returnedValue().getName())).isNotNull(); - // jpaAttempt.transacted(() -> { - // context(givenUser.getName()); - // assertThat(em.find(RbacUserEntity.class, givenUser.getUuid())) - // .isNotNull().extracting(RbacUserEntity::getName).isEqualTo(givenUser.getName()); - // }).assertSuccessful(); } } @@ -87,9 +82,6 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest { // then the user is deleted result.assertSuccessful(); assertThat(rbacUserRepository.findByName(givenUser.getName())).isNull(); - // jpaAttempt.transacted(() -> { - // assertThat(rbacUserRepository.findByName(givenUser.getName())).isNull(); - // }).assertSuccessful(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomer.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomer.java index 7d5b0b43..bb00975f 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomer.java +++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomer.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.test.cust; -import static java.util.UUID.randomUUID; public class TestCustomer { diff --git a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java index e8cfc5bb..fd51ebf8 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java @@ -16,7 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.UUID; import static java.lang.String.format; -import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -85,7 +85,8 @@ class TestPackageControllerAcceptanceTest { @Test void withDescriptionUpdatesDescription() { - assumeThat(getDescriptionOfPackage("xxx00")) + assertThat(getDescriptionOfPackage("xxx00")) + .as("precondition failed") .isEqualTo("Here you can add your own description of package xxx00."); final var randomDescription = RandomStringUtils.randomAlphanumeric(80); @@ -117,7 +118,8 @@ class TestPackageControllerAcceptanceTest { @Test void withNullDescriptionUpdatesDescriptionToNull() { - assumeThat(getDescriptionOfPackage("xxx01")) + assertThat(getDescriptionOfPackage("xxx01")) + .as("precondition failed") .isEqualTo("Here you can add your own description of package xxx01."); // @formatter:off @@ -146,7 +148,8 @@ class TestPackageControllerAcceptanceTest { @Test void withoutDescriptionDoesNothing() { - assumeThat(getDescriptionOfPackage("xxx02")) + assertThat(getDescriptionOfPackage("xxx02")) + .as("precondition failed") .isEqualTo("Here you can add your own description of package xxx02."); // @formatter:off diff --git a/src/test/java/net/hostsharing/test/JpaAttempt.java b/src/test/java/net/hostsharing/test/JpaAttempt.java index a7244e37..589049bb 100644 --- a/src/test/java/net/hostsharing/test/JpaAttempt.java +++ b/src/test/java/net/hostsharing/test/JpaAttempt.java @@ -12,7 +12,6 @@ import java.util.Optional; import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; /** * Wraps the 'when' part of a DataJpaTest to improve readability of tests. @@ -138,7 +137,7 @@ public class JpaAttempt { } public JpaResult assumeSuccessful() { - assumeThat(exception).as(firstRootCauseMessageLineOf(exception)).isNull(); + assertThat(exception).as(firstRootCauseMessageLineOf(exception)).isNull(); return this; } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 9915854e..a4f570f9 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -4,7 +4,7 @@ spring: platform: postgres datasource: - url: jdbc:tc:postgresql:13.7-bullseye:///spring_boot_testcontainers + url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers url-local: jdbc:postgresql://localhost:5432/postgres username: postgres password: password