Compare commits
7 Commits
master
...
spike/auto
Author | SHA1 | Date | |
---|---|---|---|
|
1af213a95a | ||
|
0675918362 | ||
|
e8c4946111 | ||
|
086fb11436 | ||
|
e7558cdbe8 | ||
|
c2ea66a87f | ||
|
4eceb41ebc |
5
.aliases
5
.aliases
@ -83,7 +83,7 @@ alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l'
|
|||||||
|
|
||||||
alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources'
|
alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources'
|
||||||
alias gw-test='. .aliases; ./gradlew test'
|
alias gw-test='. .aliases; ./gradlew test'
|
||||||
alias gw-check='. .aliases; gw test check -x pitest'
|
alias gw-check='. .aliases; gw test importOfficeData check -x pitest -x :dependencyCheckAnalyze'
|
||||||
|
|
||||||
# etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries
|
# etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries
|
||||||
alias gw-importOfficeData-in-docker-compose='
|
alias gw-importOfficeData-in-docker-compose='
|
||||||
@ -95,6 +95,3 @@ if [ ! -f .environment ]; then
|
|||||||
cp .tc-environment .environment
|
cp .tc-environment .environment
|
||||||
fi
|
fi
|
||||||
source .environment
|
source .environment
|
||||||
|
|
||||||
alias scenario-reports-upload='./gradlew scenarioTests convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office'
|
|
||||||
alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office'
|
|
||||||
|
84
Jenkinsfile
vendored
84
Jenkinsfile
vendored
@ -1,84 +0,0 @@
|
|||||||
pipeline {
|
|
||||||
agent {
|
|
||||||
dockerfile {
|
|
||||||
filename 'etc/jenkinsAgent.Dockerfile'
|
|
||||||
// additionalBuildArgs ...
|
|
||||||
args '--network=bridge --user root -v $PWD:$PWD -v /var/run/docker.sock:/var/run/docker.sock --group-add 984'
|
|
||||||
reuseNode true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
environment {
|
|
||||||
DOCKER_HOST = 'unix:///var/run/docker.sock'
|
|
||||||
HSADMINNG_POSTGRES_ADMIN_USERNAME = 'admin'
|
|
||||||
HSADMINNG_POSTGRES_RESTRICTED_USERNAME = 'restricted'
|
|
||||||
HSADMINNG_MIGRATION_DATA_PATH = 'migration'
|
|
||||||
}
|
|
||||||
|
|
||||||
triggers {
|
|
||||||
pollSCM('H/1 * * * *')
|
|
||||||
}
|
|
||||||
|
|
||||||
stages {
|
|
||||||
stage('Checkout') {
|
|
||||||
steps {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage ('Compile') {
|
|
||||||
steps {
|
|
||||||
sh './gradlew clean processSpring compileJava compileTestJava --no-daemon'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage ('Tests') {
|
|
||||||
parallel {
|
|
||||||
stage('Unit-/Integration/Acceptance-Tests') {
|
|
||||||
steps {
|
|
||||||
sh './gradlew check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Import-Tests') {
|
|
||||||
steps {
|
|
||||||
sh './gradlew importOfficeData importHostingAssets --no-daemon'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Scenario-Tests') {
|
|
||||||
steps {
|
|
||||||
sh './gradlew scenarioTests --no-daemon'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage ('Check') {
|
|
||||||
steps {
|
|
||||||
sh './gradlew check -x pitest -x dependencyCheckAnalyze --no-daemon'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
// archive test results
|
|
||||||
junit 'build/test-results/test/*.xml'
|
|
||||||
|
|
||||||
// archive the JaCoCo coverage report in XML and HTML format
|
|
||||||
jacoco(
|
|
||||||
execPattern: 'build/jacoco/*.exec',
|
|
||||||
classPattern: 'build/classes/java/main',
|
|
||||||
sourcePattern: 'src/main/java'
|
|
||||||
)
|
|
||||||
|
|
||||||
// archive scenario-test reports in HTML format
|
|
||||||
sh '''
|
|
||||||
./gradlew convertMarkdownToHtml
|
|
||||||
'''
|
|
||||||
archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true
|
|
||||||
|
|
||||||
// cleanup workspace
|
|
||||||
cleanWs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
README.md
12
README.md
@ -497,19 +497,9 @@ We'll see if this changes when the project progresses and more validations are a
|
|||||||
|
|
||||||
### OWASP Security Vulnerability Check
|
### OWASP Security Vulnerability Check
|
||||||
|
|
||||||
An OWASP security vulnerability is configured, but you need an API key.
|
An OWASP security vulnerability is configured and can be utilized by running:
|
||||||
Fetch it from https://nvd.nist.gov/developers/request-an-api-key.
|
|
||||||
|
|
||||||
Then add it to your `~/.gradle/gradle.properties` file:
|
|
||||||
|
|
||||||
```
|
|
||||||
OWASP_API_KEY=........-....-....-....-............
|
|
||||||
```
|
|
||||||
|
|
||||||
Now you can run the dependency vulnerability check:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
gw dependencyCheckUpdate
|
|
||||||
gw dependencyCheckAnalyze
|
gw dependencyCheckAnalyze
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
. .aliases
|
. .aliases
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
git fetch origin >/dev/null
|
echo "Checking for new commits on any branch ..."
|
||||||
|
git fetch origin
|
||||||
branch_with_new_commits=`git fetch origin >/dev/null; git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads | grep '\[behind' | cut -d' ' -f1 | head -n1`
|
branch_with_new_commits=`git fetch origin >/dev/null; git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads | grep '\[behind' | cut -d' ' -f1 | head -n1`
|
||||||
|
|
||||||
if [ -n "$branch_with_new_commits" ]; then
|
if [ -n "$branch_with_new_commits" ]; then
|
||||||
@ -19,11 +20,11 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "building ..."
|
echo "building ..."
|
||||||
./gradlew gw clean test check -x pitest
|
./gradlew test
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# wait 10s with a little animation
|
# wait 10s with a little animation
|
||||||
echo -e -n "\r\033[K waiting for changes (/) ..."
|
echo -e -n " waiting for changes (/) ..."
|
||||||
sleep 2
|
sleep 2
|
||||||
echo -e -n "\r\033[K waiting for changes (-) ..."
|
echo -e -n "\r\033[K waiting for changes (-) ..."
|
||||||
sleep 2
|
sleep 2
|
||||||
|
112
build.gradle
112
build.gradle
@ -1,10 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.3.4'
|
id 'org.springframework.boot' version '3.2.4'
|
||||||
id 'io.spring.dependency-management' version '1.1.6'
|
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.9'
|
id 'com.github.jk1.dependency-license-report' version '2.6'
|
||||||
id "org.owasp.dependencycheck" version "10.0.4"
|
id "org.owasp.dependencycheck" version "9.0.10"
|
||||||
id "com.diffplug.spotless" version "6.25.0"
|
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'
|
||||||
@ -58,20 +58,19 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
||||||
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.2'
|
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.1'
|
||||||
implementation 'org.springdoc:springdoc-openapi:2.6.0'
|
implementation 'org.springdoc:springdoc-openapi:2.4.0'
|
||||||
implementation 'org.postgresql:postgresql:42.7.4'
|
implementation 'org.postgresql:postgresql:42.7.3'
|
||||||
implementation 'org.liquibase:liquibase-core:4.29.2'
|
implementation 'org.liquibase:liquibase-core:4.27.0'
|
||||||
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.8.3'
|
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.3'
|
||||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0'
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0'
|
||||||
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.12.0'
|
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||||
implementation 'net.java.dev.jna:jna:5.15.0'
|
implementation 'net.java.dev.jna:jna:5.8.0'
|
||||||
implementation 'org.modelmapper:modelmapper:3.2.1'
|
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||||
implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
|
implementation 'org.iban4j:iban4j:3.2.7-RELEASE'
|
||||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0'
|
||||||
implementation 'org.webjars:swagger-ui:5.17.14'
|
implementation 'org.reflections:reflections:0.9.12'
|
||||||
implementation 'org.reflections:reflections:0.10.2'
|
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
testCompileOnly 'org.projectlombok:lombok'
|
testCompileOnly 'org.projectlombok:lombok'
|
||||||
@ -86,9 +85,9 @@ dependencies {
|
|||||||
testImplementation 'org.testcontainers:junit-jupiter'
|
testImplementation 'org.testcontainers:junit-jupiter'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
testImplementation 'org.testcontainers:postgresql'
|
testImplementation 'org.testcontainers:postgresql'
|
||||||
testImplementation 'com.tngtech.archunit:archunit-junit5:1.3.0'
|
testImplementation 'com.tngtech.archunit:archunit-junit5:1.2.1'
|
||||||
testImplementation 'io.rest-assured:spring-mock-mvc'
|
testImplementation 'io.rest-assured:spring-mock-mvc'
|
||||||
testImplementation 'org.hamcrest:hamcrest-core:3.0'
|
testImplementation 'org.hamcrest:hamcrest-core:2.2'
|
||||||
testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1'
|
testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||||
}
|
}
|
||||||
@ -118,8 +117,8 @@ openapiProcessor {
|
|||||||
springRoot {
|
springRoot {
|
||||||
processorName 'spring'
|
processorName 'spring'
|
||||||
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
|
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
|
||||||
apiPath "$projectDir/src/main/resources/api-definition/api-definition.yaml"
|
apiPath "$projectDir/src/main/resources/api-definition.yaml"
|
||||||
mapping "$projectDir/src/main/resources/api-definition/api-mappings.yaml"
|
mapping "$projectDir/src/main/resources/api-mappings.yaml"
|
||||||
targetDir "$buildDir/generated/sources/openapi-javax"
|
targetDir "$buildDir/generated/sources/openapi-javax"
|
||||||
showWarnings true
|
showWarnings true
|
||||||
openApiNullable true
|
openApiNullable true
|
||||||
@ -255,7 +254,7 @@ test {
|
|||||||
'net.hostsharing.hsadminng.**.generated.**',
|
'net.hostsharing.hsadminng.**.generated.**',
|
||||||
]
|
]
|
||||||
useJUnitPlatform {
|
useJUnitPlatform {
|
||||||
excludeTags 'importOfficeData', 'importHostingData', 'scenarioTest'
|
excludeTags 'import'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jacocoTestReport {
|
jacocoTestReport {
|
||||||
@ -277,7 +276,7 @@ jacocoTestCoverageVerification {
|
|||||||
violationRules {
|
violationRules {
|
||||||
rule {
|
rule {
|
||||||
limit {
|
limit {
|
||||||
minimum = 0.80 // TODO.test: improve instruction coverage
|
minimum = 0.92
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,20 +288,15 @@ jacocoTestCoverageVerification {
|
|||||||
element = 'CLASS'
|
element = 'CLASS'
|
||||||
excludes = [
|
excludes = [
|
||||||
'net.hostsharing.hsadminng.**.generated.**',
|
'net.hostsharing.hsadminng.**.generated.**',
|
||||||
'net.hostsharing.hsadminng.rbac.test.dom.TestDomainEntity',
|
|
||||||
'net.hostsharing.hsadminng.HsadminNgApplication',
|
'net.hostsharing.hsadminng.HsadminNgApplication',
|
||||||
'net.hostsharing.hsadminng.ping.PingController',
|
'net.hostsharing.hsadminng.ping.PingController',
|
||||||
'net.hostsharing.hsadminng.rbac.generator.*',
|
|
||||||
'net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService',
|
|
||||||
'net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService.Node',
|
|
||||||
'net.hostsharing.hsadminng.**.*Repository',
|
|
||||||
'net.hostsharing.hsadminng.mapper.Mapper'
|
'net.hostsharing.hsadminng.mapper.Mapper'
|
||||||
]
|
]
|
||||||
|
|
||||||
limit {
|
limit {
|
||||||
counter = 'LINE'
|
counter = 'LINE'
|
||||||
value = 'COVEREDRATIO'
|
value = 'COVEREDRATIO'
|
||||||
minimum = 0.75 // TODO.test: improve line coverage
|
minimum = 0.98
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rule {
|
rule {
|
||||||
@ -316,7 +310,7 @@ jacocoTestCoverageVerification {
|
|||||||
limit {
|
limit {
|
||||||
counter = 'BRANCH'
|
counter = 'BRANCH'
|
||||||
value = 'COVEREDRATIO'
|
value = 'COVEREDRATIO'
|
||||||
minimum = 0.00 // TODO.test: improve branch coverage
|
minimum = 1.00
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,30 +338,19 @@ tasks.register('importHostingAssets', Test) {
|
|||||||
mustRunAfter spotlessJava
|
mustRunAfter spotlessJava
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('scenarioTests', Test) {
|
|
||||||
useJUnitPlatform {
|
|
||||||
includeTags 'scenarioTest'
|
|
||||||
}
|
|
||||||
|
|
||||||
group 'verification'
|
|
||||||
description 'run the import jobs as tests'
|
|
||||||
|
|
||||||
mustRunAfter spotlessJava
|
|
||||||
}
|
|
||||||
|
|
||||||
// pitest mutation testing
|
// pitest mutation testing
|
||||||
pitest {
|
pitest {
|
||||||
targetClasses = ['net.hostsharing.hsadminng.**']
|
targetClasses = ['net.hostsharing.hsadminng.**']
|
||||||
excludedClasses = [
|
excludedClasses = [
|
||||||
'net.hostsharing.hsadminng.config.**',
|
'net.hostsharing.hsadminng.config.**',
|
||||||
// 'net.hostsharing.hsadminng.**.*Controller',
|
'net.hostsharing.hsadminng.**.*Controller',
|
||||||
'net.hostsharing.hsadminng.**.generated.**'
|
'net.hostsharing.hsadminng.**.generated.**'
|
||||||
]
|
]
|
||||||
|
|
||||||
targetTests = ['net.hostsharing.hsadminng.**.*UnitTest', 'net.hostsharing.hsadminng.**.*RestTest']
|
targetTests = ['net.hostsharing.hsadminng.**.*UnitTest', 'net.hostsharing.hsadminng.**.*RestTest']
|
||||||
excludedTestClasses = ['**AcceptanceTest*', '**IntegrationTest*']
|
excludedTestClasses = ['**AcceptanceTest*', '**IntegrationTest*']
|
||||||
|
|
||||||
pitestVersion = '1.17.0'
|
pitestVersion = '1.15.3'
|
||||||
junit5PluginVersion = '1.1.0'
|
junit5PluginVersion = '1.1.0'
|
||||||
|
|
||||||
threads = 4
|
threads = 4
|
||||||
@ -402,46 +385,3 @@ tasks.named("dependencyUpdates").configure {
|
|||||||
isNonStable(it.candidate.version)
|
isNonStable(it.candidate.version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Generate HTML from Markdown scenario-test-reports using Pandoc:
|
|
||||||
tasks.register('convertMarkdownToHtml') {
|
|
||||||
description = 'Generates HTML from Markdown scenario-test-reports using Pandoc.'
|
|
||||||
group = 'Conversion'
|
|
||||||
|
|
||||||
// Define the template file and input directory
|
|
||||||
def templateFile = file('doc/scenarios/.template.html')
|
|
||||||
|
|
||||||
// Task configuration and execution
|
|
||||||
doFirst {
|
|
||||||
// Check if pandoc is installed
|
|
||||||
try {
|
|
||||||
exec {
|
|
||||||
commandLine 'pandoc', '--version'
|
|
||||||
}
|
|
||||||
} catch (Exception) {
|
|
||||||
throw new GradleException("Pandoc is not installed or not found in the system path.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the template file exists
|
|
||||||
if (!templateFile.exists()) {
|
|
||||||
throw new GradleException("Template file 'doc/scenarios/.template.html' not found.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
// Gather all Markdown files in the current directory
|
|
||||||
fileTree(dir: '.', include: 'build/doc/scenarios/*.md').each { file ->
|
|
||||||
// Corrected way to create the output file path
|
|
||||||
def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html'))
|
|
||||||
|
|
||||||
// Execute pandoc for each markdown file
|
|
||||||
exec {
|
|
||||||
commandLine 'pandoc', file.absolutePath, '--template', templateFile.absolutePath, '-o', outputFile.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
println "Converted ${file.name} to ${outputFile.name}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
convertMarkdownToHtml.dependsOn scenarioTests
|
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
# Handling Automatic Creation of Hosting Assets for New Booking Items
|
|
||||||
|
|
||||||
**Status:**
|
|
||||||
- [x] proposed by (Michael Hönnig)
|
|
||||||
- [ ] accepted by (Participants)
|
|
||||||
- [ ] rejected by (Participants)
|
|
||||||
- [ ] superseded by (superseding ADR)
|
|
||||||
|
|
||||||
|
|
||||||
## Context and Problem Statement
|
|
||||||
|
|
||||||
When a customer creates a new booking item (e.g., `MANAGED_WEBSPACE`), the system must automatically create the related hosting asset.
|
|
||||||
This process can sometimes fail or require additional data from the user, e.g. installing a DNS verification key, or a hostmaster, e.g. the target server to use.
|
|
||||||
|
|
||||||
The challenge is how to handle this automatic creation process while dealing with missing data, asynchronicity and failures while ensuring system consistency and proper user notification.
|
|
||||||
|
|
||||||
|
|
||||||
### Technical Background
|
|
||||||
|
|
||||||
The creation of hosting assets can occur synchronously (in simple cases) or asynchronously (when additional steps like manual verification are needed).
|
|
||||||
For example, a `DOMAIN_SETUP` hosting asset may require DNS verification from the user, and until this is provided, the related domain cannot be fully set up.
|
|
||||||
|
|
||||||
Additionally, not all data needed for creating the hosting asset is stored in the booking item.
|
|
||||||
It's part of the HTTP request and later stored in the hosting asset, but we also need to store it before the hosting asset can be created asynchronously.
|
|
||||||
|
|
||||||
Current system behavior involves returning HTTP 201 upon booking item creation, but the automatic hosting asset creation might fail due to missing information.
|
|
||||||
The system needs to manage the creation process in a way that ensures valid hosting assets are created and informs the user of any actions required while still returning a 201 HTTP code, not an error code.
|
|
||||||
|
|
||||||
|
|
||||||
## Considered Options
|
|
||||||
|
|
||||||
For storing the data needed for the hosting-asset creation:
|
|
||||||
|
|
||||||
* STORAGE-1: Store temporary asset data in the `BookingItemEntity`, e.g. a JSON column.
|
|
||||||
And delete the value of that column, once the hosting assets got successfully created.
|
|
||||||
* STORAGE-2: Create hosting assets immediately, even if invalid, but mark them as "inactive" until completed and fully validated.
|
|
||||||
* STORAGE-3: Store the asset data in a kind of event- or job-queue, which get deleted once the hosting-asset got successfully created.
|
|
||||||
|
|
||||||
For the user-notification status:
|
|
||||||
|
|
||||||
* STATUS-1: Introduce a status field in the booking-items.
|
|
||||||
* STATUS-2: Store the status in the event-/job-queue entries.
|
|
||||||
|
|
||||||
### STORAGE-1: Temporary Data Storage in `BookingItemEntity`
|
|
||||||
|
|
||||||
Store asset-related data (e.g., domain name) in a temporary column or JSON field in the `BookingItemEntity` until the hosting assets are successfully created.
|
|
||||||
Once assets are created, the temporary data is deleted to avoid inconsistencies.
|
|
||||||
|
|
||||||
#### Advantages
|
|
||||||
- Easy to implement.
|
|
||||||
|
|
||||||
#### Disadvantages
|
|
||||||
- Needs either a separate map of properties in the booking-item.
|
|
||||||
- Or, if stored as a JSON field in the booking-item-resources, these are misused.
|
|
||||||
- Requires additional cleanup logic to remove stale data.
|
|
||||||
|
|
||||||
### STORAGE-2: Inactive Hosting Assets Until Validation
|
|
||||||
|
|
||||||
Create the hosting assets immediately upon booking item creation but mark them as "inactive" until all required information (e.g., verification code) is provided and validation is complete.
|
|
||||||
|
|
||||||
#### Advantages
|
|
||||||
- Avoids temporary external data storage for the hosting-assets.
|
|
||||||
|
|
||||||
#### Disadvantages
|
|
||||||
- Validation becomes more complex as some properties need to be validated, others not.
|
|
||||||
And some properties even need special treatment for new entities, which then becomes vague.
|
|
||||||
- Inactive assets have to be filtered from operational assets.
|
|
||||||
- Potential risk of incomplete or inconsistent assets being created, which may require correction.
|
|
||||||
- Difficult to write tests for all possible combinations of validations.
|
|
||||||
|
|
||||||
### STORAGE-3: Event-Based Approach
|
|
||||||
|
|
||||||
The hosting asset data required for creation us passed to the API and stored in a `BookingItemCreatedEvent`.
|
|
||||||
If hosting asset creation cannot happen synchronously, the event is stored and processed asynchronously in batches, retrying failed asset creation as needed.
|
|
||||||
|
|
||||||
#### Advantages
|
|
||||||
- Clean-data-structure (separation of concerns).
|
|
||||||
- Clear separation between booking item creation and hosting asset creation.
|
|
||||||
- Only valid assets in the database.
|
|
||||||
- Can handle complex asynchronous processes (like waiting for external verification) in a clean and structured manner.
|
|
||||||
- Easier to manage retries and failures in asset creation without complicating the booking item structure.
|
|
||||||
|
|
||||||
#### Disadvantages
|
|
||||||
- At the Spring controller level, the whole JSON is already converted into Java objects,
|
|
||||||
but for storing the asset data in the even, we need JSON again.
|
|
||||||
This could is not just a performance-overhead but could also lead to inconsistencies.
|
|
||||||
|
|
||||||
### STATUS-1: Store hosting-asset-creation-status in the `BookingItemEntity`
|
|
||||||
|
|
||||||
A status field would be added to booking-items to track the creation state of related hosting assets.
|
|
||||||
The users could check their booking-items for the status of the hosting-asset creation, error messages and further instructions.
|
|
||||||
|
|
||||||
#### Advantages
|
|
||||||
- Easy to implement.
|
|
||||||
|
|
||||||
#### Disadvantages
|
|
||||||
- Adds a field to the booking-item which is makes no sense anymore once the related hosting asset is created.
|
|
||||||
|
|
||||||
|
|
||||||
### Status-2: Store hosting-asset-creation-status in the `BookingItemCreateEvent`
|
|
||||||
|
|
||||||
A status field would be added to the booking-item-created event and get updated with the latest messages any time we try to create the hosting-asset.
|
|
||||||
|
|
||||||
#### Advantages
|
|
||||||
- Clean-data-structure (separation of concerns)
|
|
||||||
|
|
||||||
#### Disadvantages
|
|
||||||
- Accessing the status requires querying the event queue.
|
|
||||||
|
|
||||||
|
|
||||||
## Decision Outcome
|
|
||||||
|
|
||||||
**Chosen Option: STORAGE-3 with STATUS-2 (Event-Based Approach with `BookingItemCreatedEvent`)**
|
|
||||||
|
|
||||||
The event-based approach was selected as the best solution for handling automatic hosting asset creation. This option provides a clear separation between booking item creation and hosting asset creation, ensuring that no invalid or incomplete assets are created. The asynchronous nature of the event system allows for retries and external validation steps (such as user-entered verification codes) without disrupting the overall flow.
|
|
||||||
|
|
||||||
By using `BookingItemCreatedEvent` to store the hosting-asset data and the status,
|
|
||||||
we don't need to misuse other data structures for temporary data
|
|
||||||
and therefore hava a clean separation of concerns.
|
|
@ -1,124 +0,0 @@
|
|||||||
### hsadminNg fachliches Glossar
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Currently, this business glossary is only available in German because in many cases,
|
|
||||||
the German terms are important for comprehensibility for those using this software.
|
|
||||||
-->
|
|
||||||
|
|
||||||
Dieses ist eine Sammlung von Fachbegriffen, die in diesem Projekt benutzt werden.
|
|
||||||
Ebenfalls aufgenommen sind technische Begriffe, die für Benutzer für das Verständnis der Schnittstellen nötig sind.
|
|
||||||
|
|
||||||
Falls etwas fehlt, bitte Bescheid geben.
|
|
||||||
|
|
||||||
|
|
||||||
#### Partner
|
|
||||||
|
|
||||||
In diesem System ist ein _Partner_ grundsätzlich jeglicher Geschäftspartner der _Hostsharing eG_.
|
|
||||||
Dies können grundsätzlich Kunden, siehe [Debitor](#Debitor), wie Lieferanten sein.
|
|
||||||
Derzeit sind aber nur Debitoren implementiert.
|
|
||||||
|
|
||||||
Des Weiteren gibt es für jeden _Partner_ eine fünfstellige Partnernummer mit dem Prefix 'P-' (z.B. `P-123454`)
|
|
||||||
sowie Zusatzinformationen (z.B. Registergerichtnummer oder Geburtsdatum), die zur genauen Identifikation benötigt werden.
|
|
||||||
|
|
||||||
Für einen _Partner_ kann es gleichzeitig mehrere [Debitoren](#Debitor)
|
|
||||||
und zeitlich nacheinander mehrere [Mitgliedschaften](#Mitgliedschaft) geben.
|
|
||||||
|
|
||||||
Partner sind grundsätzlich als ist [Relation](#Relation) der Vertragsperson mit der Person _Hostsharing eG_ implementiert.
|
|
||||||
|
|
||||||
|
|
||||||
### Debitor
|
|
||||||
|
|
||||||
Ein `Debitor` ist quasi ein Rechnungsempfänger für einen [Partner](#Partner).
|
|
||||||
|
|
||||||
Für einen _Partner_ kann es gleichzeitig mehrere [Debitoren](#Debitor) geben,
|
|
||||||
z.B. für spezielle Projekte des Kunden oder verbundene Organisationen.
|
|
||||||
|
|
||||||
Des Weiteren gibt es für jeden _Partner_ eine fünfstellige Partnernummer mit dem Prefix 'P-' (z.B. `P-123454`)
|
|
||||||
sowie Zusatzinformationen (z.B. Registergerichtsnummer oder Geburtsdatum), die zur genauen Identifikation benötigt werden.
|
|
||||||
|
|
||||||
Debitoren sind grundsätzlich als ist [Relation](#Relation) der Vertragsperson mit der Person des Vertragspartners implementiert.
|
|
||||||
|
|
||||||
|
|
||||||
#### Relation
|
|
||||||
|
|
||||||
Eine _Relation_ ist eine typisierte und mit Kontaktdaten versehene Beziehung einer (_Holder_)-Person zu einer _Anchor_-Person.
|
|
||||||
|
|
||||||
Eine Relation ist eine Art Geschäftsrolle, wir haben hier aber keinen Begriff mit 'Rolle' verwendet,
|
|
||||||
weil 'Role' (engl.) zu leicht mit der [RBAC-Rolle](#RBAC-Role) verwechselt werden könnte.
|
|
||||||
|
|
||||||
Die _Relation_ ist auch ein technisches Konzept und gehört nicht zur Domänensprache.
|
|
||||||
Dieses Konzept ist jedoch für das Verständnis der ([API](#API)) notwendig.
|
|
||||||
|
|
||||||
|
|
||||||
#### Ex-Partner
|
|
||||||
|
|
||||||
Ex-Partner bilden [Personen](#Person) ab, die vormals [Partner](#Partner) waren.
|
|
||||||
Diese bleiben dadurch informationshalber im System verfügbar.
|
|
||||||
|
|
||||||
Implementiert ist der _Ex-Partner_ als eine besondere Form der [Relation](#Relation)
|
|
||||||
der Person des Ex-Partner (_Holder_) zum neuen Partner (_Anchor_) dargestellt.
|
|
||||||
Dieses kann zu einer Kettenbildung führen.
|
|
||||||
|
|
||||||
|
|
||||||
#### Representative-Contact (ehemals _contractual_)
|
|
||||||
|
|
||||||
Ein _Representative_ ist eine natürliche Person, die für eine nicht-natürliche Person vertretungsberechtigt ist.
|
|
||||||
|
|
||||||
Implementiert ist der _Representative_ als eine besondere Form der [Relation](#Relation)
|
|
||||||
der Person des Repräsentanten (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
|
||||||
|
|
||||||
|
|
||||||
### VIP-Contact
|
|
||||||
|
|
||||||
Ein _VIP-Contact_ ist eine natürliche Person, die für einen Geschäftspartner eine wichtige Funktion übernimmt,
|
|
||||||
nicht aber deren offizieller Repräsentant ist.
|
|
||||||
|
|
||||||
Implementiert ist der _VIP-Contact_ als eine besondere Form der [Relation](#Relation)
|
|
||||||
der Person des VIP-Contact (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
|
||||||
|
|
||||||
|
|
||||||
### Operations-Contact
|
|
||||||
|
|
||||||
Ein _Operations-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner technischer Ansprechpartner ist.
|
|
||||||
|
|
||||||
Ein Seiteneffekt ist, dass diese Person im Ticketsystem Znuny direkt dem Geschäftspartner zugeordnet werden kann.
|
|
||||||
|
|
||||||
Im Legacy System waren das die Kontakte mit der Rolle `operation` und `silent`.
|
|
||||||
|
|
||||||
Implementiert ist der _Operations-Contact_ als eine besondere Form der [Relation](#Relation)
|
|
||||||
der Person des _Operations-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
|
||||||
|
|
||||||
|
|
||||||
### OperationsAlert-Contact
|
|
||||||
|
|
||||||
Ein _OperationsAlert-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner bei technischen Probleme kontaktiert werden soll.
|
|
||||||
|
|
||||||
Im Legacy System waren das die Kontakte mit der Rolle `operation`.
|
|
||||||
|
|
||||||
Implementiert ist der _OperationsAlert-Contact_ als eine besondere Form der [Relation](#Relation)
|
|
||||||
der Person des _OperationsAlert-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
|
||||||
|
|
||||||
|
|
||||||
### Subscriber-Contact
|
|
||||||
|
|
||||||
Ein _Subscriber-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner eine bestimmte Mailingliste abonniert.
|
|
||||||
|
|
||||||
Implementiert ist der _Subscriber-Contact_ als eine besondere Form der [Relation](#Relation)
|
|
||||||
der Person des _Subscriber-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
|
||||||
Zusätzlich wird diese Relation mit dem Kurznamen der abonnierten Mailingliste markiert.
|
|
||||||
|
|
||||||
|
|
||||||
#### Anchor / Relation-Anchor
|
|
||||||
|
|
||||||
siehe [Relation](#Relation)
|
|
||||||
|
|
||||||
|
|
||||||
#### Holder / Relation-Holder
|
|
||||||
|
|
||||||
siehe [Relation](#Relation)
|
|
||||||
|
|
||||||
|
|
||||||
#### API
|
|
||||||
|
|
||||||
Und API (Application-Programming-Interface) verstehen wir eine über HTTPS angesprochene programmatisch bedienbare Schnittstell
|
|
||||||
zur Funktionalität des hsAdmin-NG-Systems.
|
|
@ -64,7 +64,7 @@ classDiagram
|
|||||||
}
|
}
|
||||||
|
|
||||||
class partner-MeierGmbH {
|
class partner-MeierGmbH {
|
||||||
+Numeric partnerNumber: P-12345
|
+Numeric partnerNumber: 12345
|
||||||
+Relation partnerRel
|
+Relation partnerRel
|
||||||
}
|
}
|
||||||
partner-MeierGmbH *-- rel-MeierGmbH
|
partner-MeierGmbH *-- rel-MeierGmbH
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html $if(lang)$ lang="$lang$" $endif$>
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
|
||||||
|
|
||||||
<!-- <link rel="stylesheet" type="text/css" href="template.css" /> -->
|
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/template.css" />
|
|
||||||
|
|
||||||
<link href="https://vjs.zencdn.net/5.4.4/video-js.css" rel="stylesheet" />
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
|
|
||||||
<!-- <script type='text/javascript' src='menu/js/jquery.cookie.js'></script> -->
|
|
||||||
<!-- <script type='text/javascript' src='menu/js/jquery.hoverIntent.minified.js'></script> -->
|
|
||||||
<!-- <script type='text/javascript' src='menu/js/jquery.dcjqaccordion.2.7.min.js'></script> -->
|
|
||||||
|
|
||||||
<!-- <link href="menu/css/skins/blue.css" rel="stylesheet" type="text/css" /> -->
|
|
||||||
<!-- <link href="menu/css/skins/graphite.css" rel="stylesheet" type="text/css" /> -->
|
|
||||||
<!-- <link href="menu/css/skins/grey.css" rel="stylesheet" type="text/css" /> -->
|
|
||||||
|
|
||||||
<!-- <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <script src="script.js"></script> -->
|
|
||||||
|
|
||||||
<!-- <script src="jquery.sticky-kit.js "></script> -->
|
|
||||||
<script type='text/javascript' src='https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/js/jquery.cookie.js'></script>
|
|
||||||
<script type='text/javascript' src='https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/js/jquery.hoverIntent.minified.js'></script>
|
|
||||||
<script type='text/javascript' src='https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/js/jquery.dcjqaccordion.2.7.min.js'></script>
|
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/css/skins/blue.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/css/skins/graphite.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/css/skins/grey.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="https://cdn.jsdelivr.net/gh/ryangrose/easy-pandoc-templates@948e28e5/css/elegant_bootstrap.css" rel="stylesheet" type="text/css" />
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/script.js"></script>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/jquery.sticky-kit.js"></script>
|
|
||||||
<meta name="generator" content="pandoc" />
|
|
||||||
$for(author-meta)$
|
|
||||||
<meta name="author" content="$author-meta$" />
|
|
||||||
$endfor$
|
|
||||||
$if(date-meta)$
|
|
||||||
<meta name="date" content="$date-meta$" />
|
|
||||||
$endif$
|
|
||||||
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>
|
|
||||||
<style type="text/css">code{white-space: pre;}</style>
|
|
||||||
$if(quotes)$
|
|
||||||
<style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style>
|
|
||||||
$endif$
|
|
||||||
$if(highlighting-css)$
|
|
||||||
<style type="text/css">
|
|
||||||
$highlighting-css$
|
|
||||||
</style>
|
|
||||||
$endif$
|
|
||||||
$for(css)$
|
|
||||||
<link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$/>
|
|
||||||
$endfor$
|
|
||||||
$if(math)$
|
|
||||||
$math$
|
|
||||||
$endif$
|
|
||||||
$for(header-includes)$
|
|
||||||
$header-includes$
|
|
||||||
$endfor$
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
|
|
||||||
$if(title)$
|
|
||||||
<div class="navbar navbar-static-top">
|
|
||||||
<div class="navbar-inner">
|
|
||||||
<div class="container">
|
|
||||||
<span class="doc-title">$title$</span>
|
|
||||||
<ul class="nav pull-right doc-info">
|
|
||||||
$for(author)$
|
|
||||||
<li><p class="navbar-text">$author$</p></li>
|
|
||||||
$endfor$
|
|
||||||
$if(date)$
|
|
||||||
<li><p class="navbar-text">$date$</p></li>
|
|
||||||
$endif$
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
$endif$
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
$if(toc)$
|
|
||||||
<div id="$idprefix$TOC" class="span3">
|
|
||||||
<div class="well toc">
|
|
||||||
|
|
||||||
$toc$
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
$endif$
|
|
||||||
<div class="span$if(toc)$9$else$12$endif$">
|
|
||||||
|
|
||||||
$if(abstract)$
|
|
||||||
<H1>$abstract-title$</H1>
|
|
||||||
$abstract$
|
|
||||||
$endif$
|
|
||||||
|
|
||||||
$for(include-before)$
|
|
||||||
$include-before$
|
|
||||||
$endfor$
|
|
||||||
$body$
|
|
||||||
$for(include-after)$
|
|
||||||
$include-after$
|
|
||||||
$endfor$
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="https://vjs.zencdn.net/5.4.4/video.js"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1 +0,0 @@
|
|||||||
find the generated ScenarioReports in build/doc/scenarios
|
|
@ -90,20 +90,6 @@ Acceptance-tests, are blackbox-tests and do <u>not</u> count into test-code-cove
|
|||||||
TODO.test: Complete the Acceptance-Tests test concept.
|
TODO.test: Complete the Acceptance-Tests test concept.
|
||||||
|
|
||||||
|
|
||||||
#### Scenario-Tests
|
|
||||||
|
|
||||||
Our Scenario-tests are induced by business use-cases.
|
|
||||||
They test from the REST API all the way down to the database.
|
|
||||||
|
|
||||||
Most scenario-tests are positive tests, they test if business scenarios do work.
|
|
||||||
But few might be negative tests, which test if specific forbidden data gets rejected.
|
|
||||||
|
|
||||||
Our scenario tests also generate test-reports which contain the REST-API calls needed for each scenario.
|
|
||||||
These reports can be used as examples for the API usage from a business perspective.
|
|
||||||
|
|
||||||
There is an extra document regarding scenario-test, see [Scenario-Tests README](../src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md).
|
|
||||||
|
|
||||||
|
|
||||||
#### Performance-Tests
|
#### Performance-Tests
|
||||||
|
|
||||||
Performance-critical scenarios have to be identified and a special performance-test has to be implemented.
|
Performance-critical scenarios have to be identified and a special performance-test has to be implemented.
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
{
|
{
|
||||||
"allowedLicenses": [
|
"allowedLicenses": [
|
||||||
{ "moduleLicense": "Apache 2" },
|
|
||||||
{ "moduleLicense": "Apache 2.0" },
|
{ "moduleLicense": "Apache 2.0" },
|
||||||
{ "moduleLicense": "Apache-2.0" },
|
{ "moduleLicense": "Apache 2" },
|
||||||
{ "moduleLicense": "Apache License 2.0" },
|
{ "moduleLicense": "Apache License 2.0" },
|
||||||
{ "moduleLicense": "Apache License v2.0" },
|
|
||||||
{ "moduleLicense": "Apache License, Version 2.0" },
|
{ "moduleLicense": "Apache License, Version 2.0" },
|
||||||
{ "moduleLicense": "The Apache Software License, Version 2.0" },
|
{ "moduleLicense": "The Apache Software License, Version 2.0" },
|
||||||
|
|
||||||
@ -13,8 +11,6 @@
|
|||||||
{ "moduleLicense": "BSD-3-Clause" },
|
{ "moduleLicense": "BSD-3-Clause" },
|
||||||
{ "moduleLicense": "The BSD License" },
|
{ "moduleLicense": "The BSD License" },
|
||||||
|
|
||||||
{ "moduleLicense": "The New BSD License" },
|
|
||||||
|
|
||||||
{ "moduleLicense": "CDDL 1.1" },
|
{ "moduleLicense": "CDDL 1.1" },
|
||||||
{ "moduleLicense": "CDDL/GPLv2+CE" },
|
{ "moduleLicense": "CDDL/GPLv2+CE" },
|
||||||
{ "moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0" },
|
{ "moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0" },
|
||||||
@ -33,22 +29,11 @@
|
|||||||
{ "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception" },
|
{ "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception" },
|
||||||
{ "moduleLicense": "GPL2 w/ CPE" },
|
{ "moduleLicense": "GPL2 w/ CPE" },
|
||||||
|
|
||||||
{ "moduleLicense": "LGPL, version 2.1"},
|
|
||||||
{ "moduleLicense": "LGPL-2.1-or-later"},
|
|
||||||
|
|
||||||
{ "moduleLicense": "MIT License" },
|
{ "moduleLicense": "MIT License" },
|
||||||
{ "moduleLicense": "MIT" },
|
{ "moduleLicense": "MIT" },
|
||||||
{ "moduleLicense": "The MIT License (MIT)" },
|
{ "moduleLicense": "The MIT License (MIT)" },
|
||||||
{ "moduleLicense": "The MIT License" },
|
{ "moduleLicense": "The MIT License" },
|
||||||
|
|
||||||
{ "moduleLicense": "WTFPL" },
|
{ "moduleName": "org.springdoc:springdoc-openapi" }
|
||||||
|
|
||||||
{
|
|
||||||
"moduleLicense": null,
|
|
||||||
"#moduleLicense": "Apache License 2.0, see https://github.com/springdoc/springdoc-openapi/blob/main/LICENSE",
|
|
||||||
"moduleVersion": "2.4.0",
|
|
||||||
"moduleName": "org.springdoc:springdoc-openapi"
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
FROM eclipse-temurin:21-jdk
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y bind9-utils pandoc && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
|||||||
<?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[
|
||||||
|
Cyclic references are not possible if file comes in JSON text format.
|
||||||
|
]]></notes>
|
||||||
|
<packageUrl regex="true">^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$</packageUrl>
|
||||||
|
<cpe>cpe:/a:fasterxml:jackson-databind</cpe>
|
||||||
|
</suppress>
|
||||||
<suppress>
|
<suppress>
|
||||||
<notes><![CDATA[
|
<notes><![CDATA[
|
||||||
Internal tooling, not exposed to the Internet.
|
Internal tooling, not exposed to the Internet.
|
||||||
@ -7,10 +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[
|
|
||||||
Malicious HTTP redirect in JAXB on a REST-endpoint is not that dangerous.
|
|
||||||
]]></notes>
|
|
||||||
<cve>CVE-2024-9329</cve>
|
|
||||||
</suppress>
|
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.config;
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import org.openapitools.jackson.nullable.JsonNullableModule;
|
import org.openapitools.jackson.nullable.JsonNullableModule;
|
||||||
@ -10,20 +9,15 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class JsonObjectMapperConfiguration {
|
public class JsonObjectMapperConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
public Jackson2ObjectMapperBuilder customObjectMapper() {
|
public Jackson2ObjectMapperBuilder customObjectMapper() {
|
||||||
// HOWTO: add JSON converters and specify other JSON mapping configurations
|
|
||||||
return new Jackson2ObjectMapperBuilder()
|
return new Jackson2ObjectMapperBuilder()
|
||||||
.modules(new JsonNullableModule(), new JavaTimeModule())
|
.modules(new JsonNullableModule(), new JavaTimeModule())
|
||||||
.featuresToEnable(
|
.featuresToEnable(JsonParser.Feature.ALLOW_COMMENTS)
|
||||||
JsonParser.Feature.ALLOW_COMMENTS,
|
|
||||||
DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS
|
|
||||||
)
|
|
||||||
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ public class CustomErrorResponse {
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
this.statusCode = status.value();
|
this.statusCode = status.value();
|
||||||
this.statusPhrase = status.getReasonPhrase();
|
this.statusPhrase = status.getReasonPhrase();
|
||||||
// HOWTO: debug serverside error response - set a breakpoint here
|
|
||||||
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
|
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public final class HashGenerator {
|
|||||||
"abcdefghijklmnopqrstuvwxyz" +
|
"abcdefghijklmnopqrstuvwxyz" +
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||||
"0123456789/.";
|
"0123456789/.";
|
||||||
private static boolean couldBeHashEnabled; // TODO.legacy: remove after legacy data is migrated
|
private static boolean couldBeHashEnabled; // TODO.impl: remove after legacy data is migrated
|
||||||
|
|
||||||
public enum Algorithm {
|
public enum Algorithm {
|
||||||
LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"),
|
LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"),
|
||||||
|
@ -5,8 +5,8 @@ import lombok.Builder;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
@ -14,7 +14,7 @@ import jakarta.persistence.Id;
|
|||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
// a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity
|
// a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.springframework.context.ApplicationEvent;
|
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
public class BookingItemCreatedAppEvent extends ApplicationEvent {
|
|
||||||
|
|
||||||
private BookingItemCreatedEventEntity entity;
|
|
||||||
|
|
||||||
public BookingItemCreatedAppEvent(
|
|
||||||
@NotNull final Object source,
|
|
||||||
@NotNull final HsBookingItemRealEntity newBookingItem,
|
|
||||||
final String assetJson) {
|
|
||||||
super(source);
|
|
||||||
this.entity = new BookingItemCreatedEventEntity(newBookingItem, assetJson);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.ToString;
|
|
||||||
import lombok.experimental.SuperBuilder;
|
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.JoinColumn;
|
|
||||||
import jakarta.persistence.ManyToOne;
|
|
||||||
import jakarta.persistence.MapsId;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import jakarta.persistence.Version;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(schema = "hs_booking", name = "item_created_event")
|
|
||||||
@SuperBuilder(toBuilder = true)
|
|
||||||
@Getter
|
|
||||||
@ToString
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class BookingItemCreatedEventEntity implements BaseEntity {
|
|
||||||
@Id
|
|
||||||
@Column(name="bookingitemuuid")
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@MapsId
|
|
||||||
@ManyToOne(optional = false)
|
|
||||||
@JoinColumn(name = "bookingitemuuid", nullable = false)
|
|
||||||
private HsBookingItemRealEntity bookingItem;
|
|
||||||
|
|
||||||
@Version
|
|
||||||
private int version;
|
|
||||||
|
|
||||||
@Column(name = "assetjson")
|
|
||||||
private String assetJson;
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@Column(name = "statusmessage")
|
|
||||||
private String statusMessage;
|
|
||||||
|
|
||||||
public BookingItemCreatedEventEntity(
|
|
||||||
@NotNull final HsBookingItemRealEntity newBookingItem,
|
|
||||||
final String assetJson) {
|
|
||||||
this.bookingItem = newBookingItem;
|
|
||||||
this.assetJson = assetJson;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item;
|
|
||||||
|
|
||||||
import org.springframework.data.repository.Repository;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public interface BookingItemCreatedEventRepository extends Repository<BookingItemCreatedEventEntity, UUID> {
|
|
||||||
|
|
||||||
BookingItemCreatedEventEntity save(HsBookingItemRealEntity current);
|
|
||||||
|
|
||||||
BookingItemCreatedEventEntity findByBookingItem(HsBookingItemRealEntity newBookingItem);
|
|
||||||
}
|
|
@ -14,9 +14,9 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProject;
|
|||||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
@ -45,7 +45,7 @@ import static java.util.Optional.ofNullable;
|
|||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item;
|
package net.hostsharing.hsadminng.hs.booking.item;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsApi;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsApi;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
||||||
@ -14,7 +12,6 @@ import net.hostsharing.hsadminng.mapper.KeyValueMap;
|
|||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@ -25,7 +22,6 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -37,15 +33,9 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StrictMapper mapper;
|
private StrictMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApplicationEventPublisher applicationEventPublisher;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsBookingItemRbacRepository bookingItemRepo;
|
private HsBookingItemRbacRepository bookingItemRepo;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper jsonMapper;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EntityManagerWrapper em;
|
private EntityManagerWrapper em;
|
||||||
|
|
||||||
@ -73,8 +63,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||||
final var saveProcessor = new BookingItemEntitySaveProcessor(em, entityToSave);
|
final var mapped = new BookingItemEntitySaveProcessor(em, entityToSave)
|
||||||
final var mapped = saveProcessor
|
|
||||||
.preprocessEntity()
|
.preprocessEntity()
|
||||||
.validateEntity()
|
.validateEntity()
|
||||||
.prepareForSave()
|
.prepareForSave()
|
||||||
@ -82,7 +71,6 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
.validateContext()
|
.validateContext()
|
||||||
.mapUsing(e -> mapper.map(e, HsBookingItemResource.class, ITEM_TO_RESOURCE_POSTMAPPER))
|
.mapUsing(e -> mapper.map(e, HsBookingItemResource.class, ITEM_TO_RESOURCE_POSTMAPPER))
|
||||||
.revampProperties();
|
.revampProperties();
|
||||||
publishSavedEvent(saveProcessor, body);
|
|
||||||
|
|
||||||
final var uri =
|
final var uri =
|
||||||
MvcUriComponentsBuilder.fromController(getClass())
|
MvcUriComponentsBuilder.fromController(getClass())
|
||||||
@ -142,16 +130,6 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishSavedEvent(final BookingItemEntitySaveProcessor saveProcessor, final HsBookingItemInsertResource body) {
|
|
||||||
try {
|
|
||||||
final var bookingItemRealEntity = em.getReference(HsBookingItemRealEntity.class, saveProcessor.getEntity().getUuid());
|
|
||||||
applicationEventPublisher.publishEvent(new BookingItemCreatedAppEvent(
|
|
||||||
this, bookingItemRealEntity, jsonMapper.writeValueAsString(body.getHostingAsset())));
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final BiConsumer<HsBookingItem, HsBookingItemResource> ITEM_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
final BiConsumer<HsBookingItem, HsBookingItemResource> ITEM_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||||
resource.setValidFrom(entity.getValidity().lower());
|
resource.setValidFrom(entity.getValidity().lower());
|
||||||
if (entity.getValidity().hasUpperBound()) {
|
if (entity.getValidity().hasUpperBound()) {
|
||||||
@ -163,9 +141,6 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
|
|
||||||
final BiConsumer<HsBookingItemInsertResource, HsBookingItemRbacEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsBookingItemInsertResource, HsBookingItemRbacEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
entity.setProject(em.find(HsBookingProjectRealEntity.class, resource.getProjectUuid()));
|
entity.setProject(em.find(HsBookingProjectRealEntity.class, resource.getProjectUuid()));
|
||||||
ofNullable(resource.getParentItemUuid())
|
|
||||||
.map(parentItemUuid -> em.find(HsBookingItemRealEntity.class, parentItemUuid))
|
|
||||||
.ifPresent(entity::setParentItem);
|
|
||||||
entity.setValidity(toPostgresDateRange(LocalDate.now(), resource.getValidTo()));
|
entity.setValidity(toPostgresDateRange(LocalDate.now(), resource.getValidTo()));
|
||||||
entity.putResources(KeyValueMap.from(resource.getResources()));
|
entity.putResources(KeyValueMap.from(resource.getResources()));
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemResource;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
||||||
@ -21,11 +20,7 @@ public class BookingItemEntitySaveProcessor {
|
|||||||
private final HsEntityValidator<HsBookingItem> validator;
|
private final HsEntityValidator<HsBookingItem> validator;
|
||||||
private String expectedStep = "preprocessEntity";
|
private String expectedStep = "preprocessEntity";
|
||||||
private final EntityManager em;
|
private final EntityManager em;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private HsBookingItem entity;
|
private HsBookingItem entity;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private HsBookingItemResource resource;
|
private HsBookingItemResource resource;
|
||||||
|
|
||||||
public BookingItemEntitySaveProcessor(final EntityManager em, final HsBookingItem entity) {
|
public BookingItemEntitySaveProcessor(final EntityManager em, final HsBookingItem entity) {
|
||||||
@ -48,7 +43,7 @@ public class BookingItemEntitySaveProcessor {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO.legacy: remove once the migration of legacy data is done
|
// TODO.impl: remove once the migration of legacy data is done
|
||||||
/// validates the entity itself including its properties, but ignoring some error messages for import of legacy data
|
/// validates the entity itself including its properties, but ignoring some error messages for import of legacy data
|
||||||
public BookingItemEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) {
|
public BookingItemEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) {
|
||||||
step("validateEntity", "prepareForSave");
|
step("validateEntity", "prepareForSave");
|
||||||
|
@ -13,21 +13,27 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns.REGISTRA
|
|||||||
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
||||||
|
|
||||||
class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
||||||
public static final String DOMAIN_NAME_PROPERTY_NAME = "domainName";
|
|
||||||
|
|
||||||
public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}";
|
public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}";
|
||||||
|
public static final String DOMAIN_NAME_PROPERTY_NAME = "domainName";
|
||||||
|
public static final String TARGET_UNIX_USER_PROPERTY_NAME = "targetUnixUser";
|
||||||
public static final String WEBSPACE_NAME_REGEX = "[a-z][a-z0-9]{2}[0-9]{2}";
|
public static final String WEBSPACE_NAME_REGEX = "[a-z][a-z0-9]{2}[0-9]{2}";
|
||||||
public static final String TARGET_UNIX_USER_NAME_REGEX = "^"+WEBSPACE_NAME_REGEX+"$|^"+WEBSPACE_NAME_REGEX+"-[a-z0-9\\._-]+$";
|
public static final String TARGET_UNIX_USER_NAME_REGEX = "^"+WEBSPACE_NAME_REGEX+"$|^"+WEBSPACE_NAME_REGEX+"-[a-z0-9\\._-]+$";
|
||||||
public static final String VERIFICATION_CODE_PROPERTY_NAME = "verificationCode";
|
public static final String VERIFICATION_CODE_PROPERTY_NAME = "verificationCode";
|
||||||
|
|
||||||
HsDomainSetupBookingItemValidator() {
|
HsDomainSetupBookingItemValidator() {
|
||||||
super(
|
super(
|
||||||
// TODO.spec: feels wrong
|
|
||||||
stringProperty(DOMAIN_NAME_PROPERTY_NAME).writeOnce()
|
stringProperty(DOMAIN_NAME_PROPERTY_NAME).writeOnce()
|
||||||
.maxLength(253)
|
.maxLength(253)
|
||||||
.matchesRegEx(FQDN_REGEX).describedAs("is not a (non-top-level) fully qualified domain name")
|
.matchesRegEx(FQDN_REGEX).describedAs("is not a (non-top-level) fully qualified domain name")
|
||||||
.notMatchesRegEx(REGISTRAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name")
|
.notMatchesRegEx(REGISTRAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name")
|
||||||
.required(),
|
.required(),
|
||||||
|
// TODO.legacy: remove the following property once we give up legacy compatibility
|
||||||
|
stringProperty(TARGET_UNIX_USER_PROPERTY_NAME).writeOnce()
|
||||||
|
.maxLength(253)
|
||||||
|
.matchesRegEx(TARGET_UNIX_USER_NAME_REGEX).describedAs("is not a valid unix-user name")
|
||||||
|
.writeOnce()
|
||||||
|
.required(),
|
||||||
stringProperty(VERIFICATION_CODE_PROPERTY_NAME)
|
stringProperty(VERIFICATION_CODE_PROPERTY_NAME)
|
||||||
.minLength(12)
|
.minLength(12)
|
||||||
.maxLength(64)
|
.maxLength(64)
|
||||||
@ -49,11 +55,6 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String generateVerificationCode(final EntityManager em, final PropertiesProvider propertiesProvider) {
|
private static String generateVerificationCode(final EntityManager em, final PropertiesProvider propertiesProvider) {
|
||||||
final var userDefinedVerificationCode = propertiesProvider.getDirectValue(VERIFICATION_CODE_PROPERTY_NAME, String.class);
|
|
||||||
if (userDefinedVerificationCode != null) {
|
|
||||||
return userDefinedVerificationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
final var alphaNumeric = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
final var alphaNumeric = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||||
final var secureRandom = new SecureRandom();
|
final var secureRandom = new SecureRandom();
|
||||||
final var sb = new StringBuilder();
|
final var sb = new StringBuilder();
|
||||||
|
@ -3,15 +3,31 @@ package net.hostsharing.hsadminng.hs.booking.project;
|
|||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
|
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@Getter
|
@Getter
|
||||||
@ -50,4 +66,50 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
|
|||||||
return ofNullable(debitor).map(HsBookingDebitorEntity::toShortString).orElse("D-???????") +
|
return ofNullable(debitor).map(HsBookingDebitorEntity::toShortString).orElse("D-???????") +
|
||||||
":" + caption;
|
":" + caption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RbacView rbac() {
|
||||||
|
return rbacViewFor("project", HsBookingProjectRbacEntity.class)
|
||||||
|
.withIdentityView(SQL.query("""
|
||||||
|
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName
|
||||||
|
FROM hs_booking.project bookingProject
|
||||||
|
JOIN hs_office.debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
|
||||||
|
"""))
|
||||||
|
.withRestrictedViewOrderBy(SQL.expression("caption"))
|
||||||
|
.withUpdatableColumns("version", "caption")
|
||||||
|
|
||||||
|
.importEntityAlias("debitor", HsOfficeDebitorEntity.class, usingDefaultCase(),
|
||||||
|
dependsOnColumn("debitorUuid"),
|
||||||
|
directlyFetchedByDependsOnColumn(),
|
||||||
|
NOT_NULL)
|
||||||
|
|
||||||
|
.importEntityAlias("debitorRel", HsOfficeRelationRbacEntity.class, usingCase(DEBITOR),
|
||||||
|
dependsOnColumn("debitorUuid"),
|
||||||
|
fetchedBySql("""
|
||||||
|
SELECT ${columns}
|
||||||
|
FROM hs_office.relation debitorRel
|
||||||
|
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||||
|
WHERE debitor.uuid = ${REF}.debitorUuid
|
||||||
|
"""),
|
||||||
|
NOT_NULL)
|
||||||
|
.toRole("debitorRel", ADMIN).grantPermission(INSERT)
|
||||||
|
.toRole(GLOBAL, ADMIN).grantPermission(DELETE)
|
||||||
|
|
||||||
|
.createRole(OWNER, (with) -> {
|
||||||
|
with.incomingSuperRole("debitorRel", AGENT).unassumed();
|
||||||
|
})
|
||||||
|
.createSubRole(ADMIN, (with) -> {
|
||||||
|
with.permission(UPDATE);
|
||||||
|
})
|
||||||
|
.createSubRole(AGENT)
|
||||||
|
.createSubRole(TENANT, (with) -> {
|
||||||
|
with.outgoingSubRole("debitorRel", TENANT);
|
||||||
|
with.permission(SELECT);
|
||||||
|
})
|
||||||
|
|
||||||
|
.limitDiagramTo("project", "debitorRel", "rbac.global");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
rbac().generateWithBaseFileName("6-hs-booking/620-booking-project/6203-hs-booking-project-rbac");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,9 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProject;
|
|||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
@ -42,7 +42,7 @@ import java.util.Optional;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@Getter
|
@Getter
|
||||||
@ -89,9 +89,10 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity<HsHost
|
|||||||
@JoinColumn(name = "alarmcontactuuid")
|
@JoinColumn(name = "alarmcontactuuid")
|
||||||
private HsOfficeContactRealEntity alarmContact;
|
private HsOfficeContactRealEntity alarmContact;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
|
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
|
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
|
||||||
private List<HsHostingAssetRealEntity> subHostingAssets;
|
private List<HsHostingAssetRealEntity> subHostingAssets = new ArrayList<>();
|
||||||
|
|
||||||
@Column(name = "identifier")
|
@Column(name = "identifier")
|
||||||
private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc
|
private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc
|
||||||
@ -124,13 +125,6 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity<HsHost
|
|||||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config).assign(newConfig);
|
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config).assign(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<HsHostingAssetRealEntity> getSubHostingAssets() {
|
|
||||||
if (subHostingAssets == null) {
|
|
||||||
subHostingAssets = new ArrayList<>();
|
|
||||||
}
|
|
||||||
return subHostingAssets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PatchableMapWrapper<Object> directProps() {
|
public PatchableMapWrapper<Object> directProps() {
|
||||||
return getConfig();
|
return getConfig();
|
||||||
|
@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset;
|
|||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.Repository;
|
import org.springframework.data.repository.Repository;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -14,18 +13,6 @@ public interface HsHostingAssetRealRepository extends HsHostingAssetRepository<H
|
|||||||
|
|
||||||
List<HsHostingAssetRealEntity> findByIdentifier(String assetIdentifier);
|
List<HsHostingAssetRealEntity> findByIdentifier(String assetIdentifier);
|
||||||
|
|
||||||
default List<HsHostingAssetRealEntity> findByTypeAndIdentifier(@NotNull HsHostingAssetType type, @NotNull String identifier) {
|
|
||||||
return findByTypeAndIdentifierImpl(type.name(), identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Query("""
|
|
||||||
select ha
|
|
||||||
from HsHostingAssetRealEntity ha
|
|
||||||
where cast(ha.type as String) = :type
|
|
||||||
and ha.identifier = :identifier
|
|
||||||
""")
|
|
||||||
List<HsHostingAssetRealEntity> findByTypeAndIdentifierImpl(@NotNull String type, @NotNull String identifier);
|
|
||||||
|
|
||||||
@Query(value = """
|
@Query(value = """
|
||||||
select ha.uuid,
|
select ha.uuid,
|
||||||
ha.alarmcontactuuid,
|
ha.alarmcontactuuid,
|
||||||
|
@ -1,159 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetSubInsertResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetTypeResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.lambda.Reducer;
|
|
||||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
|
||||||
import net.hostsharing.hsadminng.mapper.ToStringConverter;
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
|
|
||||||
import jakarta.validation.ValidationException;
|
|
||||||
import java.net.IDN;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_HTTP_SETUP;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_MBOX_SETUP;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP;
|
|
||||||
|
|
||||||
public class DomainSetupHostingAssetFactory extends HostingAssetFactory {
|
|
||||||
|
|
||||||
public DomainSetupHostingAssetFactory(
|
|
||||||
final EntityManagerWrapper emw,
|
|
||||||
final HsBookingItemRealEntity newBookingItemRealEntity,
|
|
||||||
final HsHostingAssetAutoInsertResource asset,
|
|
||||||
final StandardMapper standardMapper) {
|
|
||||||
super(emw, newBookingItemRealEntity, asset, standardMapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HsHostingAsset create() {
|
|
||||||
final var domainSetupAsset = createDomainSetupAsset(getDomainName());
|
|
||||||
final var subHostingAssets = domainSetupAsset.getSubHostingAssets();
|
|
||||||
|
|
||||||
// TODO.legacy: as long as we need to be compatible, we always do all technical domain-setups
|
|
||||||
|
|
||||||
final var domainHttpSetupAssetResource = findSubHostingAssetResource(HsHostingAssetTypeResource.DOMAIN_HTTP_SETUP);
|
|
||||||
final var assignedToUnixUserAssetEntity = domainHttpSetupAssetResource
|
|
||||||
.map(HsHostingAssetSubInsertResource::getAssignedToAssetUuid)
|
|
||||||
.map(uuid -> emw.find(HsHostingAssetRealEntity.class, uuid))
|
|
||||||
.orElseThrow(() -> new ValidationException("DOMAIN_HTTP_SETUP subAsset with assignedToAssetUuid required in compatibility mode"));
|
|
||||||
|
|
||||||
subHostingAssets.add(
|
|
||||||
createDomainSubSetupAssetEntity(
|
|
||||||
domainSetupAsset,
|
|
||||||
DOMAIN_HTTP_SETUP,
|
|
||||||
builder -> builder
|
|
||||||
.assignedToAsset(assignedToUnixUserAssetEntity)
|
|
||||||
.identifier(getDomainName() + "|HTTP")
|
|
||||||
.caption("HTTP-Setup für " + IDN.toUnicode(getDomainName())))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Do not add to subHostingAssets in compatibility mode, in this case, DNS setup works via file system.
|
|
||||||
// The entity is created just for validation purposes.
|
|
||||||
createDomainSubSetupAssetEntity(
|
|
||||||
domainSetupAsset,
|
|
||||||
DOMAIN_DNS_SETUP,
|
|
||||||
builder -> builder
|
|
||||||
.assignedToAsset(assignedToUnixUserAssetEntity.getParentAsset())
|
|
||||||
.identifier(getDomainName() + "|DNS")
|
|
||||||
.caption("DNS-Setup für " + IDN.toUnicode(getDomainName())));
|
|
||||||
|
|
||||||
subHostingAssets.add(
|
|
||||||
createDomainSubSetupAssetEntity(
|
|
||||||
domainSetupAsset,
|
|
||||||
DOMAIN_MBOX_SETUP,
|
|
||||||
builder -> builder
|
|
||||||
.assignedToAsset(assignedToUnixUserAssetEntity.getParentAsset())
|
|
||||||
.identifier(getDomainName() + "|MBOX")
|
|
||||||
.caption("MBOX-Setup für " + IDN.toUnicode(getDomainName())))
|
|
||||||
);
|
|
||||||
|
|
||||||
subHostingAssets.add(
|
|
||||||
createDomainSubSetupAssetEntity(
|
|
||||||
domainSetupAsset,
|
|
||||||
DOMAIN_SMTP_SETUP,
|
|
||||||
builder -> builder
|
|
||||||
.assignedToAsset(assignedToUnixUserAssetEntity.getParentAsset())
|
|
||||||
.identifier(getDomainName() + "|SMTP")
|
|
||||||
.caption("SMTP-Setup für " + IDN.toUnicode(getDomainName())))
|
|
||||||
);
|
|
||||||
|
|
||||||
return domainSetupAsset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HsHostingAssetRealEntity createDomainSetupAsset(final String domainName) {
|
|
||||||
return HsHostingAssetRealEntity.builder()
|
|
||||||
.bookingItem(fromBookingItem)
|
|
||||||
.type(HsHostingAssetType.DOMAIN_SETUP)
|
|
||||||
.identifier(domainName)
|
|
||||||
.caption(asset.getCaption() != null ? asset.getCaption() : domainName)
|
|
||||||
.alarmContact(ref(HsOfficeContactRealEntity.class, asset.getAlarmContactUuid()))
|
|
||||||
// the sub-hosting-assets get added later
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private HsHostingAssetRealEntity createDomainSubSetupAssetEntity(
|
|
||||||
final HsHostingAssetRealEntity domainSetupAsset,
|
|
||||||
final HsHostingAssetType subAssetType,
|
|
||||||
final Function<HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>, HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>> builderTransformer) {
|
|
||||||
final var resourceType = HsHostingAssetTypeResource.valueOf(subAssetType.name());
|
|
||||||
|
|
||||||
final var subAssetResourceOptional = findSubHostingAssetResource(resourceType);
|
|
||||||
|
|
||||||
subAssetResourceOptional.ifPresentOrElse(
|
|
||||||
this::verifyNotOverspecified,
|
|
||||||
() -> { throw new ValidationException("sub-asset of type " + resourceType.name() + " required in legacy mode, but missing"); }
|
|
||||||
);
|
|
||||||
|
|
||||||
return builderTransformer.apply(
|
|
||||||
HsHostingAssetRealEntity.builder()
|
|
||||||
.type(subAssetType)
|
|
||||||
.parentAsset(domainSetupAsset))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<HsHostingAssetSubInsertResource> findSubHostingAssetResource(final HsHostingAssetTypeResource resourceType) {
|
|
||||||
return getSubHostingAssetResources().stream()
|
|
||||||
.filter(ha -> ha.getType() == resourceType)
|
|
||||||
.reduce(Reducer::toSingleElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO.legacy: while we need to stay compatible, only default values can be used, thus only the type can be specified
|
|
||||||
private void verifyNotOverspecified(final HsHostingAssetSubInsertResource givenSubAssetResource) {
|
|
||||||
final var convert = new ToStringConverter().ignoring("assignedToAssetUuid");
|
|
||||||
final var expectedSubAssetResource = new HsHostingAssetSubInsertResource();
|
|
||||||
expectedSubAssetResource.setType(givenSubAssetResource.getType());
|
|
||||||
if ( !convert.from(givenSubAssetResource).equals(convert.from(expectedSubAssetResource)) ) {
|
|
||||||
throw new ValidationException("sub asset " + givenSubAssetResource.getType() + " is over-specified, in compatibility mode, only default values allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDomainName() {
|
|
||||||
return asset.getIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<HsHostingAssetSubInsertResource> getSubHostingAssetResources() {
|
|
||||||
return asset.getSubHostingAssets();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void persist(final HsHostingAsset newHostingAsset) {
|
|
||||||
super.persist(newHostingAsset);
|
|
||||||
newHostingAsset.getSubHostingAssets().forEach(super::persist);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T ref(final Class<T> entityClass, final UUID uuid) {
|
|
||||||
return uuid != null ? emw.getReference(entityClass, uuid) : null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
|
|
||||||
|
|
||||||
import jakarta.validation.ValidationException;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
|
|
||||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
abstract class HostingAssetFactory {
|
|
||||||
|
|
||||||
final EntityManagerWrapper emw;
|
|
||||||
final HsBookingItemRealEntity fromBookingItem;
|
|
||||||
final HsHostingAssetAutoInsertResource asset;
|
|
||||||
final StandardMapper standardMapper;
|
|
||||||
|
|
||||||
protected abstract HsHostingAsset create();
|
|
||||||
|
|
||||||
public String createAndPersist() {
|
|
||||||
try {
|
|
||||||
final HsHostingAsset newHostingAsset = create();
|
|
||||||
persist(newHostingAsset);
|
|
||||||
return null;
|
|
||||||
} catch (final ValidationException exc) {
|
|
||||||
return exc.getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void persist(final HsHostingAsset newHostingAsset) {
|
|
||||||
new HostingAssetEntitySaveProcessor(emw, newHostingAsset)
|
|
||||||
.preprocessEntity()
|
|
||||||
.validateEntity()
|
|
||||||
.prepareForSave()
|
|
||||||
.save()
|
|
||||||
.validateContext();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import jakarta.validation.ValidationException;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
|
||||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.ApplicationListener;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class HsBookingItemCreatedListener implements ApplicationListener<BookingItemCreatedAppEvent> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EntityManagerWrapper emw;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper jsonMapper;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private StandardMapper standardMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public void onApplicationEvent(@NotNull BookingItemCreatedAppEvent bookingItemCreatedAppEvent) {
|
|
||||||
if (containsAssetJson(bookingItemCreatedAppEvent)) {
|
|
||||||
createRelatedHostingAsset(bookingItemCreatedAppEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean containsAssetJson(final BookingItemCreatedAppEvent bookingItemCreatedAppEvent) {
|
|
||||||
return bookingItemCreatedAppEvent.getEntity().getAssetJson() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createRelatedHostingAsset(final BookingItemCreatedAppEvent event) throws JsonProcessingException {
|
|
||||||
final var newBookingItemRealEntity = event.getEntity().getBookingItem();
|
|
||||||
final var asset = jsonMapper.readValue(event.getEntity().getAssetJson(), HsHostingAssetAutoInsertResource.class);
|
|
||||||
final var factory = switch (newBookingItemRealEntity.getType()) {
|
|
||||||
case PRIVATE_CLOUD, CLOUD_SERVER, MANAGED_SERVER ->
|
|
||||||
forNowNoAutomaticHostingAssetCreationPossible(emw, newBookingItemRealEntity, asset, standardMapper);
|
|
||||||
case MANAGED_WEBSPACE -> new ManagedWebspaceHostingAssetFactory(emw, newBookingItemRealEntity, asset, standardMapper);
|
|
||||||
case DOMAIN_SETUP -> new DomainSetupHostingAssetFactory(emw, newBookingItemRealEntity, asset, standardMapper);
|
|
||||||
};
|
|
||||||
if (factory != null) {
|
|
||||||
final var statusMessage = factory.createAndPersist();
|
|
||||||
// TODO.impl: once we implement retry, we need to amend this code (persist/merge/delete)
|
|
||||||
if (statusMessage != null) {
|
|
||||||
event.getEntity().setStatusMessage(statusMessage);
|
|
||||||
emw.persist(event.getEntity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private HostingAssetFactory forNowNoAutomaticHostingAssetCreationPossible(
|
|
||||||
final EntityManagerWrapper emw,
|
|
||||||
final HsBookingItemRealEntity fromBookingItem,
|
|
||||||
final HsHostingAssetAutoInsertResource asset,
|
|
||||||
final StandardMapper standardMapper
|
|
||||||
) {
|
|
||||||
return new HostingAssetFactory(emw, fromBookingItem, asset, standardMapper) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HsHostingAsset create() {
|
|
||||||
// TODO.impl: we should validate the asset JSON, but some violations are un-avoidable at that stage
|
|
||||||
throw new ValidationException("waiting for manual setup of hosting asset for booking item of type " + fromBookingItem.getType());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetTypeResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
|
|
||||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
|
|
||||||
import jakarta.validation.ValidationException;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
|
|
||||||
public class ManagedWebspaceHostingAssetFactory extends HostingAssetFactory {
|
|
||||||
|
|
||||||
public ManagedWebspaceHostingAssetFactory(
|
|
||||||
final EntityManagerWrapper emw,
|
|
||||||
final HsBookingItemRealEntity newBookingItemRealEntity,
|
|
||||||
final HsHostingAssetAutoInsertResource asset,
|
|
||||||
final StandardMapper standardMapper) {
|
|
||||||
super(emw, newBookingItemRealEntity, asset, standardMapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HsHostingAsset create() {
|
|
||||||
if (asset.getType() != HsHostingAssetTypeResource.MANAGED_WEBSPACE) {
|
|
||||||
throw new ValidationException("requires MANAGED_WEBSPACE hosting asset, but got " +
|
|
||||||
Optional.of(asset)
|
|
||||||
.map(HsHostingAssetAutoInsertResource::getType)
|
|
||||||
.map(Enum::name)
|
|
||||||
.orElse(null));
|
|
||||||
}
|
|
||||||
final var managedWebspaceHostingAsset = standardMapper.map(asset, HsHostingAssetRealEntity.class);
|
|
||||||
managedWebspaceHostingAsset.setBookingItem(fromBookingItem);
|
|
||||||
emw.createQuery(
|
|
||||||
"SELECT asset FROM HsHostingAssetRealEntity asset WHERE asset.bookingItem.uuid=:bookingItemUuid",
|
|
||||||
HsHostingAssetRealEntity.class)
|
|
||||||
.setParameter("bookingItemUuid", fromBookingItem.getParentItem().getUuid())
|
|
||||||
.getResultStream().findFirst()
|
|
||||||
.ifPresent(managedWebspaceHostingAsset::setParentAsset);
|
|
||||||
|
|
||||||
return managedWebspaceHostingAsset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void persist(final HsHostingAsset newManagedWebspaceHostingAsset) {
|
|
||||||
super.persist(newManagedWebspaceHostingAsset);
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,7 +42,7 @@ public class HostingAssetEntitySaveProcessor {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO.legacy: remove once the migration of legacy data is done
|
// TODO.impl: remove once the migration of legacy data is done
|
||||||
/// validates the entity itself including its properties, but ignoring some error messages for import of legacy data
|
/// validates the entity itself including its properties, but ignoring some error messages for import of legacy data
|
||||||
public HostingAssetEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) {
|
public HostingAssetEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) {
|
||||||
step("validateEntity", "prepareForSave");
|
step("validateEntity", "prepareForSave");
|
||||||
|
@ -15,7 +15,7 @@ import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanPro
|
|||||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
||||||
|
|
||||||
// TODO.legacy: make package private once we've migrated the legacy data
|
// TODO.impl: make package private once we've migrated the legacy data
|
||||||
public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
||||||
|
|
||||||
// according to RFC 1035 (section 5) and RFC 1034
|
// according to RFC 1035 (section 5) and RFC 1034
|
||||||
@ -33,7 +33,7 @@ public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityVal
|
|||||||
RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
|
RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
|
||||||
public static final String IDENTIFIER_SUFFIX = "|DNS";
|
public static final String IDENTIFIER_SUFFIX = "|DNS";
|
||||||
|
|
||||||
private static List<String> zoneFileErrors = null; // TODO.legacy: remove once legacy data is migrated
|
private static List<String> zoneFileErrors = null; // TODO.impl: remove once legacy data is migrated
|
||||||
|
|
||||||
HsDomainDnsSetupHostingAssetValidator() {
|
HsDomainDnsSetupHostingAssetValidator() {
|
||||||
super(
|
super(
|
||||||
|
@ -3,10 +3,10 @@ package net.hostsharing.hsadminng.hs.office.bankaccount;
|
|||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -16,7 +16,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.*;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "bankaccount_rv")
|
@Table(schema = "hs_office", name = "bankaccount_rv")
|
||||||
|
@ -11,9 +11,9 @@ import lombok.experimental.FieldNameConstants;
|
|||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@Getter
|
@Getter
|
||||||
@ -54,14 +54,8 @@ public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContac
|
|||||||
@Column(name = "caption")
|
@Column(name = "caption")
|
||||||
private String caption;
|
private String caption;
|
||||||
|
|
||||||
@Builder.Default
|
|
||||||
@Setter(AccessLevel.NONE)
|
|
||||||
@Type(JsonType.class)
|
|
||||||
@Column(name = "postaladdress")
|
@Column(name = "postaladdress")
|
||||||
private Map<String, String> postalAddress = new HashMap<>();
|
private String postalAddress; // multiline free-format text
|
||||||
|
|
||||||
@Transient
|
|
||||||
private PatchableMapWrapper<String> postalAddressWrapper;
|
|
||||||
|
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
@Setter(AccessLevel.NONE)
|
@Setter(AccessLevel.NONE)
|
||||||
@ -81,17 +75,6 @@ public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContac
|
|||||||
@Transient
|
@Transient
|
||||||
private PatchableMapWrapper<String> phoneNumbersWrapper;
|
private PatchableMapWrapper<String> phoneNumbersWrapper;
|
||||||
|
|
||||||
public PatchableMapWrapper<String> getPostalAddress() {
|
|
||||||
return PatchableMapWrapper.of(
|
|
||||||
postalAddressWrapper,
|
|
||||||
(newWrapper) -> {postalAddressWrapper = newWrapper;},
|
|
||||||
postalAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putPostalAddress(Map<String, String> newPostalAddress) {
|
|
||||||
getPostalAddress().assign(newPostalAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PatchableMapWrapper<String> getEmailAddresses() {
|
public PatchableMapWrapper<String> getEmailAddresses() {
|
||||||
return PatchableMapWrapper.of(
|
return PatchableMapWrapper.of(
|
||||||
emailAddressesWrapper,
|
emailAddressesWrapper,
|
||||||
|
@ -18,8 +18,7 @@ class HsOfficeContactEntityPatcher implements EntityPatcher<HsOfficeContactPatch
|
|||||||
@Override
|
@Override
|
||||||
public void apply(final HsOfficeContactPatchResource resource) {
|
public void apply(final HsOfficeContactPatchResource resource) {
|
||||||
OptionalFromJson.of(resource.getCaption()).ifPresent(entity::setCaption);
|
OptionalFromJson.of(resource.getCaption()).ifPresent(entity::setCaption);
|
||||||
Optional.ofNullable(resource.getPostalAddress())
|
OptionalFromJson.of(resource.getPostalAddress()).ifPresent(entity::setPostalAddress);
|
||||||
.ifPresent(r -> entity.getPostalAddress().patch(KeyValueMap.from(resource.getPostalAddress())));
|
|
||||||
Optional.ofNullable(resource.getEmailAddresses())
|
Optional.ofNullable(resource.getEmailAddresses())
|
||||||
.ifPresent(r -> entity.getEmailAddresses().patch(KeyValueMap.from(resource.getEmailAddresses())));
|
.ifPresent(r -> entity.getEmailAddresses().patch(KeyValueMap.from(resource.getEmailAddresses())));
|
||||||
Optional.ofNullable(resource.getPhoneNumbers())
|
Optional.ofNullable(resource.getPhoneNumbers())
|
||||||
|
@ -36,7 +36,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> getListOfCoopAssets(
|
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> listCoopAssets(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID membershipUuid,
|
final UUID membershipUuid,
|
||||||
@ -55,7 +55,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> postNewCoopAssetTransaction(
|
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
||||||
@ -77,7 +77,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getSingleCoopAssetTransactionByUuid(
|
|
||||||
|
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getCoopAssetTransactionByUuid(
|
||||||
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) {
|
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) {
|
||||||
|
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
@ -128,9 +129,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
if ( resource.getRevertedAssetTxUuid() != null ) {
|
if ( resource.getReverseEntryUuid() != null ) {
|
||||||
entity.setRevertedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
entity.setAdjustedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getReverseEntryUuid())
|
||||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getRevertedAssetTxUuid()))));
|
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getReverseEntryUuid()))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -8,10 +8,10 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@ -31,7 +31,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "coopassettx_rv")
|
@Table(schema = "hs_office", name = "coopassettx_rv")
|
||||||
@ -50,8 +50,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
|
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
|
||||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
|
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
|
||||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
|
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
|
||||||
.withProp(at -> ofNullable(at.getRevertedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getAdjustedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||||
.withProp(at -> ofNullable(at.getReversalAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getAdjustmentAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||||
.quotedValues(false);
|
.quotedValues(false);
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -77,7 +77,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
* The signed value which directly affects the booking balance.
|
* The signed value which directly affects the booking balance.
|
||||||
*
|
*
|
||||||
* <p>This means, that a DEPOSIT is always positive, a DISBURSAL is always negative,
|
* <p>This means, that a DEPOSIT is always positive, a DISBURSAL is always negative,
|
||||||
* but an REVERSAL can bei either positive or negative.
|
* but an ADJUSTMENT can bei either positive or negative.
|
||||||
* See {@link HsOfficeCoopAssetsTransactionType} for</p> more information.
|
* See {@link HsOfficeCoopAssetsTransactionType} for</p> more information.
|
||||||
*/
|
*/
|
||||||
@Column(name = "assetvalue")
|
@Column(name = "assetvalue")
|
||||||
@ -96,14 +96,14 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
private String comment;
|
private String comment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally, the UUID of the corresponding transaction for an reversal transaction.
|
* Optionally, the UUID of the corresponding transaction for an adjustment transaction.
|
||||||
*/
|
*/
|
||||||
@OneToOne
|
@OneToOne
|
||||||
@JoinColumn(name = "revertedassettxuuid")
|
@JoinColumn(name = "adjustedassettxuuid")
|
||||||
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "revertedAssetTx")
|
@OneToOne(mappedBy = "adjustedAssetTx")
|
||||||
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity adjustmentAssetTx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HsOfficeCoopAssetsTransactionEntity load() {
|
public HsOfficeCoopAssetsTransactionEntity load() {
|
||||||
|
@ -4,7 +4,7 @@ public enum HsOfficeCoopAssetsTransactionType {
|
|||||||
/**
|
/**
|
||||||
* correction of wrong bookings, value can be positive or negative
|
* correction of wrong bookings, value can be positive or negative
|
||||||
*/
|
*/
|
||||||
REVERSAL,
|
ADJUSTMENT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* payment received from member after signing shares, value >0
|
* payment received from member after signing shares, value >0
|
||||||
|
@ -38,7 +38,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> getListOfCoopShares(
|
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> listCoopShares(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID membershipUuid,
|
final UUID membershipUuid,
|
||||||
@ -57,7 +57,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> postNewCoopSharesTransaction(
|
public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
||||||
@ -80,7 +80,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getSingleCoopShareTransactionByUuid(
|
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getCoopShareTransactionByUuid(
|
||||||
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) {
|
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) {
|
||||||
|
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
@ -131,9 +131,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
if ( resource.getRevertedShareTxUuid() != null ) {
|
if ( resource.getAdjustedShareTxUuid() != null ) {
|
||||||
entity.setRevertedShareTx(coopSharesTransactionRepo.findByUuid(resource.getRevertedShareTxUuid())
|
entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid())
|
||||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedShareTxUuid %s not found".formatted(resource.getRevertedShareTxUuid()))));
|
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid()))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ import lombok.Setter;
|
|||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -29,7 +29,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "coopsharetx_rv")
|
@Table(schema = "hs_office", name = "coopsharetx_rv")
|
||||||
@ -48,8 +48,8 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
|||||||
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
|
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
|
||||||
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
|
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
|
||||||
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
|
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
|
||||||
.withProp(at -> ofNullable(at.getRevertedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getAdjustedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||||
.withProp(at -> ofNullable(at.getReversalShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getAdjustmentShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||||
.quotedValues(false);
|
.quotedValues(false);
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -71,7 +71,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
|||||||
* The signed value which directly affects the booking balance.
|
* The signed value which directly affects the booking balance.
|
||||||
*
|
*
|
||||||
* <p>This means, that a SUBSCRIPTION is always positive, a CANCELLATION is always negative,
|
* <p>This means, that a SUBSCRIPTION is always positive, a CANCELLATION is always negative,
|
||||||
* but an REVERSAL can bei either positive or negative.
|
* but an ADJUSTMENT can bei either positive or negative.
|
||||||
* See {@link HsOfficeCoopSharesTransactionType} for</p> more information.
|
* See {@link HsOfficeCoopSharesTransactionType} for</p> more information.
|
||||||
*/
|
*/
|
||||||
@Column(name = "valuedate")
|
@Column(name = "valuedate")
|
||||||
@ -93,14 +93,14 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
|||||||
private String comment;
|
private String comment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally, the UUID of the corresponding transaction for a REVERSAL transaction.
|
* Optionally, the UUID of the corresponding transaction for an adjustment transaction.
|
||||||
*/
|
*/
|
||||||
@OneToOne
|
@OneToOne
|
||||||
@JoinColumn(name = "revertedsharetxuuid")
|
@JoinColumn(name = "adjustedsharetxuuid")
|
||||||
private HsOfficeCoopSharesTransactionEntity revertedShareTx;
|
private HsOfficeCoopSharesTransactionEntity adjustedShareTx;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "revertedShareTx")
|
@OneToOne(mappedBy = "adjustedShareTx")
|
||||||
private HsOfficeCoopSharesTransactionEntity reversalShareTx;
|
private HsOfficeCoopSharesTransactionEntity adjustmentShareTx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HsOfficeCoopSharesTransactionEntity load() {
|
public HsOfficeCoopSharesTransactionEntity load() {
|
||||||
|
@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
|||||||
|
|
||||||
public enum HsOfficeCoopSharesTransactionType {
|
public enum HsOfficeCoopSharesTransactionType {
|
||||||
/**
|
/**
|
||||||
* reversal of wrong bookings, with either positive or negative value identical to reversed transaction
|
* correction of wrong bookings, with either positive or negative value
|
||||||
*/
|
*/
|
||||||
REVERSAL,
|
ADJUSTMENT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shares signed, e.g. with the declaration of accession, value >0
|
* shares signed, e.g. with the declaration of accession, value >0
|
||||||
|
@ -22,10 +22,8 @@ import jakarta.persistence.PersistenceContext;
|
|||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
||||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
|
||||||
@ -51,24 +49,24 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<HsOfficeDebitorResource>> getListOfDebitors(
|
public ResponseEntity<List<HsOfficeDebitorResource>> listDebitors(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final String name,
|
final String name,
|
||||||
final String debitorNumber) {
|
final Integer debitorNumber) {
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final var entities = debitorNumber != null
|
final var entities = debitorNumber != null
|
||||||
? debitorRepo.findDebitorByDebitorNumber(cropTag("D-", debitorNumber))
|
? debitorRepo.findDebitorByDebitorNumber(debitorNumber)
|
||||||
: debitorRepo.findDebitorByOptionalNameLike(name);
|
: debitorRepo.findDebitorByOptionalNameLike(name);
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var resources = mapper.mapList(entities, HsOfficeDebitorResource.class);
|
||||||
return ResponseEntity.ok(resources);
|
return ResponseEntity.ok(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<HsOfficeDebitorResource> postNewDebitor(
|
public ResponseEntity<HsOfficeDebitorResource> addDebitor(
|
||||||
String currentSubject,
|
String currentSubject,
|
||||||
String assumedRoles,
|
String assumedRoles,
|
||||||
HsOfficeDebitorInsertResource body) {
|
HsOfficeDebitorInsertResource body) {
|
||||||
@ -79,13 +77,16 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
|
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
|
||||||
Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
|
Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
|
||||||
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none");
|
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none");
|
||||||
|
Validate.isTrue(body.getDebitorRel() == null ||
|
||||||
|
body.getDebitorRel().getType() == null || DEBITOR.name().equals(body.getDebitorRel().getType()),
|
||||||
|
"ERROR: [400] debitorRel.type must be '"+DEBITOR.name()+"' or null for default");
|
||||||
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getMark() == null,
|
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getMark() == null,
|
||||||
"ERROR: [400] debitorRel.mark must be null");
|
"ERROR: [400] debitorRel.mark must be null");
|
||||||
|
|
||||||
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
|
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
|
||||||
if (body.getDebitorRel() != null) {
|
if ( body.getDebitorRel() != null ) {
|
||||||
|
body.getDebitorRel().setType(DEBITOR.name());
|
||||||
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
||||||
debitorRel.setType(DEBITOR);
|
|
||||||
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
||||||
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
||||||
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
||||||
@ -94,10 +95,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
||||||
debitorRelOptional.ifPresentOrElse(
|
debitorRelOptional.ifPresentOrElse(
|
||||||
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
|
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
|
||||||
() -> {
|
() -> { throw new ValidationException("Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());});
|
||||||
throw new ValidationException(
|
|
||||||
"Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final var savedEntity = debitorRepo.save(entityToSave);
|
final var savedEntity = debitorRepo.save(entityToSave);
|
||||||
@ -109,13 +107,13 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
.path("/api/hs/office/debitors/{id}")
|
.path("/api/hs/office/debitors/{id}")
|
||||||
.buildAndExpand(savedEntity.getUuid())
|
.buildAndExpand(savedEntity.getUuid())
|
||||||
.toUri();
|
.toUri();
|
||||||
final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class);
|
||||||
return ResponseEntity.created(uri).body(mapped);
|
return ResponseEntity.created(uri).body(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<HsOfficeDebitorResource> getSingleDebitorByUuid(
|
public ResponseEntity<HsOfficeDebitorResource> getDebitorByUuid(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID debitorUuid) {
|
final UUID debitorUuid) {
|
||||||
@ -126,7 +124,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER));
|
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeDebitorResource.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -161,11 +159,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
|
|
||||||
final var saved = debitorRepo.save(current);
|
final var saved = debitorRepo.save(current);
|
||||||
Hibernate.initialize(saved);
|
Hibernate.initialize(saved);
|
||||||
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
|
||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsOfficeDebitorEntity, HsOfficeDebitorResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
|
||||||
resource.setDebitorNumber(entity.getTaggedDebitorNumber());
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,11 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
|||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
import org.hibernate.annotations.JoinFormula;
|
import org.hibernate.annotations.JoinFormula;
|
||||||
import org.hibernate.annotations.NotFound;
|
import org.hibernate.annotations.NotFound;
|
||||||
@ -51,7 +51,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "debitor_rv")
|
@Table(schema = "hs_office", name = "debitor_rv")
|
||||||
@ -142,14 +142,19 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTaggedDebitorNumber() {
|
private String getDebitorNumberString() {
|
||||||
return ofNullable(partner)
|
return ofNullable(partner)
|
||||||
.filter(partner -> debitorNumberSuffix != null)
|
.filter(partner -> debitorNumberSuffix != null)
|
||||||
.map(HsOfficePartnerEntity::getPartnerNumber)
|
.map(HsOfficePartnerEntity::getPartnerNumber)
|
||||||
.map(partnerNumber -> DEBITOR_NUMBER_TAG + partnerNumber + debitorNumberSuffix)
|
.map(Object::toString)
|
||||||
|
.map(partnerNumber -> partnerNumber + debitorNumberSuffix)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getDebitorNumber() {
|
||||||
|
return ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return stringify.apply(this);
|
return stringify.apply(this);
|
||||||
@ -157,7 +162,7 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return getTaggedDebitorNumber();
|
return DEBITOR_NUMBER_TAG + getDebitorNumberString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
|
@ -16,16 +16,13 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
|
|||||||
JOIN HsOfficePartnerEntity partner
|
JOIN HsOfficePartnerEntity partner
|
||||||
ON partner.partnerRel.holder = debitor.debitorRel.anchor
|
ON partner.partnerRel.holder = debitor.debitorRel.anchor
|
||||||
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
|
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
|
||||||
WHERE partner.partnerNumber = :partnerNumber
|
WHERE cast(partner.partnerNumber as integer) = :partnerNumber
|
||||||
AND debitor.debitorNumberSuffix = :debitorNumberSuffix
|
AND cast(debitor.debitorNumberSuffix as integer) = :debitorNumberSuffix
|
||||||
""")
|
""")
|
||||||
List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int partnerNumber, String debitorNumberSuffix);
|
List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int partnerNumber, byte debitorNumberSuffix);
|
||||||
|
|
||||||
default List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int debitorNumber) {
|
default List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int debitorNumber) {
|
||||||
final var partnerNumber = debitorNumber / 100;
|
return findDebitorByDebitorNumber( debitorNumber/100, (byte) (debitorNumber%100));
|
||||||
final String suffix = String.format("%02d", debitorNumber % 100);
|
|
||||||
final var result = findDebitorByDebitorNumber(partnerNumber, suffix);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
|
@ -16,10 +16,8 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
|
||||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
|
||||||
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -33,18 +31,16 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<HsOfficeMembershipResource>> getListOfMemberships(
|
public ResponseEntity<List<HsOfficeMembershipResource>> listMemberships(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID partnerUuid,
|
UUID partnerUuid,
|
||||||
final String memberNumber) {
|
Integer memberNumber) {
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final var entities = (memberNumber != null)
|
final var entities = ( memberNumber != null)
|
||||||
? ofNullable(membershipRepo.findMembershipByMemberNumber(
|
? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber))
|
||||||
cropTag(HsOfficeMembershipEntity.MEMBER_NUMBER_TAG, memberNumber))).stream()
|
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
|
||||||
.toList()
|
|
||||||
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
|
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,
|
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,
|
||||||
SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER);
|
SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
@ -53,7 +49,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<HsOfficeMembershipResource> postNewMembership(
|
public ResponseEntity<HsOfficeMembershipResource> addMembership(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final HsOfficeMembershipInsertResource body) {
|
final HsOfficeMembershipInsertResource body) {
|
||||||
@ -76,7 +72,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<HsOfficeMembershipResource> getSingleMembershipByUuid(
|
public ResponseEntity<HsOfficeMembershipResource> getMembershipByUuid(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID membershipUuid) {
|
final UUID membershipUuid) {
|
||||||
@ -127,7 +123,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsOfficeMembershipEntity, HsOfficeMembershipResource> SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
final BiConsumer<HsOfficeMembershipEntity, HsOfficeMembershipResource> SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||||
resource.setMemberNumber(entity.getTaggedMemberNumber());
|
// TODO.refa: this should be possible via ModelMapper config
|
||||||
resource.setValidFrom(entity.getValidity().lower());
|
resource.setValidFrom(entity.getValidity().lower());
|
||||||
if (entity.getValidity().hasUpperBound()) {
|
if (entity.getValidity().hasUpperBound()) {
|
||||||
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
||||||
|
@ -9,12 +9,12 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
@ -53,7 +53,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "membership_rv")
|
@Table(schema = "hs_office", name = "membership_rv")
|
||||||
@ -130,7 +130,6 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
|
|||||||
}
|
}
|
||||||
return validity;
|
return validity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getMemberNumber() {
|
public Integer getMemberNumber() {
|
||||||
if (partner == null || partner.getPartnerNumber() == null || memberNumberSuffix == null ) {
|
if (partner == null || partner.getPartnerNumber() == null || memberNumberSuffix == null ) {
|
||||||
return null;
|
return null;
|
||||||
@ -139,10 +138,6 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
|
|||||||
return getPartner().getPartnerNumber() * 100 + Integer.parseInt(memberNumberSuffix, 10);
|
return getPartner().getPartnerNumber() * 100 + Integer.parseInt(memberNumberSuffix, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTaggedMemberNumber() {
|
|
||||||
return MEMBER_NUMBER_TAG + getMemberNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return stringify.apply(this);
|
return stringify.apply(this);
|
||||||
|
@ -14,16 +14,14 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
|
|||||||
|
|
||||||
HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity);
|
HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity);
|
||||||
|
|
||||||
List<HsOfficeMembershipEntity> findAll();
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT membership FROM HsOfficeMembershipEntity membership
|
SELECT membership FROM HsOfficeMembershipEntity membership
|
||||||
WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL
|
WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL
|
||||||
OR membership.partner.uuid = :partnerUuid )
|
OR membership.partner.uuid = :partnerUuid )
|
||||||
ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix
|
ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix
|
||||||
""")
|
""")
|
||||||
List<HsOfficeMembershipEntity> findMembershipsByOptionalPartnerUuid(UUID partnerUuid);
|
List<HsOfficeMembershipEntity> findMembershipsByOptionalPartnerUuid(UUID partnerUuid);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT membership FROM HsOfficeMembershipEntity membership
|
SELECT membership FROM HsOfficeMembershipEntity membership
|
||||||
WHERE (:partnerNumber = membership.partner.partnerNumber)
|
WHERE (:partnerNumber = membership.partner.partnerNumber)
|
||||||
@ -33,12 +31,10 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
|
|||||||
HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix(
|
HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix(
|
||||||
@NotNull Integer partnerNumber,
|
@NotNull Integer partnerNumber,
|
||||||
@NotNull String suffix);
|
@NotNull String suffix);
|
||||||
|
|
||||||
default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) {
|
default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) {
|
||||||
final var partnerNumber = memberNumber / 100;
|
final var partnerNumber = memberNumber / 100;
|
||||||
final String suffix = String.format("%02d", memberNumber % 100);
|
final var suffix = memberNumber % 100;
|
||||||
final var result = findMembershipByPartnerNumberAndSuffix(partnerNumber, suffix);
|
return findMembershipByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix));
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
|||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
|
||||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -27,7 +27,6 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.EX_PARTNER;
|
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.EX_PARTNER;
|
||||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
|
||||||
@ -144,14 +143,13 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
|
|
||||||
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
|
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
|
||||||
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
|
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
|
||||||
// TODO.impl: we also need to use the new partner-person as the anchor
|
|
||||||
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
|
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
|
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
|
||||||
final var entityToSave = new HsOfficePartnerEntity();
|
final var entityToSave = new HsOfficePartnerEntity();
|
||||||
entityToSave.setPartnerNumber(cropTag(HsOfficePartnerEntity.PARTNER_NUMBER_TAG, body.getPartnerNumber()));
|
entityToSave.setPartnerNumber(body.getPartnerNumber());
|
||||||
entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
|
entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
|
||||||
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
|
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
|
||||||
return entityToSave;
|
return entityToSave;
|
||||||
|
@ -2,11 +2,11 @@ package net.hostsharing.hsadminng.hs.office.partner;
|
|||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -17,7 +17,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "partner_details_rv")
|
@Table(schema = "hs_office", name = "partner_details_rv")
|
||||||
|
@ -10,12 +10,12 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact;
|
|||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.NotFound;
|
import org.hibernate.annotations.NotFound;
|
||||||
import org.hibernate.annotations.NotFoundAction;
|
import org.hibernate.annotations.NotFoundAction;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "partner_rv")
|
@Table(schema = "hs_office", name = "partner_rv")
|
||||||
|
@ -3,11 +3,11 @@ package net.hostsharing.hsadminng.hs.office.person;
|
|||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@ -19,9 +19,8 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
// TODO.refa: split HsOfficePersonEntity into Real+Rbac-Entity
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "person_rv")
|
@Table(schema = "hs_office", name = "person_rv")
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -4,7 +4,6 @@ import jakarta.persistence.AttributeConverter;
|
|||||||
import jakarta.persistence.Converter;
|
import jakarta.persistence.Converter;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
// HOWTO: convert data types for exchange between PostgreSQL and Java/Hibernate/JPA-Entities
|
|
||||||
@Converter(autoApply = true)
|
@Converter(autoApply = true)
|
||||||
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {
|
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {
|
||||||
|
|
||||||
|
@ -5,15 +5,15 @@ import lombok.experimental.FieldNameConstants;
|
|||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
|
@ -37,7 +37,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
private HsOfficePersonRepository holderRepo;
|
private HsOfficePersonRepository holderRepo;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeContactRealRepository realContactRepo;
|
private HsOfficeContactRealRepository contactrealRepo;
|
||||||
|
|
||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
@ -48,16 +48,11 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID personUuid,
|
final UUID personUuid,
|
||||||
final HsOfficeRelationTypeResource relationType,
|
final HsOfficeRelationTypeResource relationType) {
|
||||||
final String personData,
|
|
||||||
final String contactData) {
|
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final List<HsOfficeRelationRbacEntity> entities =
|
final var entities = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
|
||||||
relationRbacRepo.findRelationRelatedToPersonUuidRelationTypePersonAndContactData(
|
mapper.map(relationType, HsOfficeRelationType.class));
|
||||||
personUuid,
|
|
||||||
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()),
|
|
||||||
personData, contactData);
|
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsOfficeRelationResource.class,
|
final var resources = mapper.mapList(entities, HsOfficeRelationResource.class,
|
||||||
RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
|
RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
@ -82,7 +77,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
entityToSave.setHolder(holderRepo.findByUuid(body.getHolderUuid()).orElseThrow(
|
entityToSave.setHolder(holderRepo.findByUuid(body.getHolderUuid()).orElseThrow(
|
||||||
() -> new NoSuchElementException("cannot find Person by holderUuid: " + body.getHolderUuid())
|
() -> new NoSuchElementException("cannot find Person by holderUuid: " + body.getHolderUuid())
|
||||||
));
|
));
|
||||||
entityToSave.setContact(realContactRepo.findByUuid(body.getContactUuid()).orElseThrow(
|
entityToSave.setContact(contactrealRepo.findByUuid(body.getContactUuid()).orElseThrow(
|
||||||
() -> new NoSuchElementException("cannot find Contact by contactUuid: " + body.getContactUuid())
|
() -> new NoSuchElementException("cannot find Contact by contactUuid: " + body.getContactUuid())
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -149,6 +144,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final BiConsumer<HsOfficeRelationRbacEntity, HsOfficeRelationResource> RELATION_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
final BiConsumer<HsOfficeRelationRbacEntity, HsOfficeRelationResource> RELATION_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||||
resource.setAnchor(mapper.map(entity.getAnchor(), HsOfficePersonResource.class));
|
resource.setAnchor(mapper.map(entity.getAnchor(), HsOfficePersonResource.class));
|
||||||
resource.setHolder(mapper.map(entity.getHolder(), HsOfficePersonResource.class));
|
resource.setHolder(mapper.map(entity.getHolder(), HsOfficePersonResource.class));
|
||||||
|
@ -12,62 +12,26 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
|
|||||||
|
|
||||||
Optional<HsOfficeRelationRbacEntity> findByUuid(UUID id);
|
Optional<HsOfficeRelationRbacEntity> findByUuid(UUID id);
|
||||||
|
|
||||||
@Query(value = """
|
default List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
|
||||||
SELECT p.* FROM hs_office.relation_rv AS p
|
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType.toString());
|
||||||
WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
|
|
||||||
""", nativeQuery = true)
|
|
||||||
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds relations by a conjunction of optional criteria, including anchorPerson, holderPerson and contact data.
|
|
||||||
* *
|
|
||||||
* @param personUuid the optional UUID of the anchorPerson or holderPerson
|
|
||||||
* @param relationType the type of the relation
|
|
||||||
* @param personData a string to match the persons tradeName, familyName or givenName (use '%' for wildcard), case ignored
|
|
||||||
* @param contactData a string to match the contacts caption, postalAddress, emailAddresses or phoneNumbers (use '%' for wildcard), case ignored
|
|
||||||
* @return a list of (accessible) relations which match all given criteria
|
|
||||||
*/
|
|
||||||
default List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidRelationTypePersonAndContactData(
|
|
||||||
final UUID personUuid,
|
|
||||||
final HsOfficeRelationType relationType,
|
|
||||||
final String personData,
|
|
||||||
final String contactData) {
|
|
||||||
return findRelationRelatedToPersonUuidRelationTypePersonAndContactDataImpl(
|
|
||||||
personUuid, toStringOrNull(relationType), toSqlLikeOperand(personData), toSqlLikeOperand(contactData));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query(value = """
|
@Query(value = """
|
||||||
SELECT rel FROM HsOfficeRelationRbacEntity AS rel
|
SELECT p.* FROM hs_office.relation_rv AS p
|
||||||
WHERE (:relationType IS NULL OR CAST(rel.type AS String) = :relationType)
|
WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
|
||||||
AND ( :personUuid IS NULL
|
""", nativeQuery = true)
|
||||||
OR rel.anchor.uuid = :personUuid OR rel.holder.uuid = :personUuid )
|
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
|
||||||
AND ( :personData IS NULL
|
|
||||||
OR lower(rel.anchor.tradeName) LIKE :personData OR lower(rel.holder.tradeName) LIKE :personData
|
@Query(value = """
|
||||||
OR lower(rel.anchor.familyName) LIKE :personData OR lower(rel.holder.familyName) LIKE :personData
|
SELECT p.* FROM hs_office.relation_rv AS p
|
||||||
OR lower(rel.anchor.givenName) LIKE :personData OR lower(rel.holder.givenName) LIKE :personData )
|
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS hs_office.RelationType))
|
||||||
AND ( :contactData IS NULL
|
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
|
||||||
OR lower(rel.contact.caption) LIKE :contactData
|
""", nativeQuery = true)
|
||||||
OR lower(CAST(rel.contact.postalAddress AS String)) LIKE :contactData
|
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
|
||||||
OR lower(CAST(rel.contact.emailAddresses AS String)) LIKE :contactData
|
|
||||||
OR lower(CAST(rel.contact.phoneNumbers AS String)) LIKE :contactData )
|
|
||||||
""")
|
|
||||||
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidRelationTypePersonAndContactDataImpl(
|
|
||||||
final UUID personUuid,
|
|
||||||
final String relationType,
|
|
||||||
final String personData,
|
|
||||||
final String contactData);
|
|
||||||
|
|
||||||
HsOfficeRelationRbacEntity save(final HsOfficeRelationRbacEntity entity);
|
HsOfficeRelationRbacEntity save(final HsOfficeRelationRbacEntity entity);
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
|
||||||
int deleteByUuid(UUID uuid);
|
int deleteByUuid(UUID uuid);
|
||||||
|
|
||||||
private static String toSqlLikeOperand(final String text) {
|
|
||||||
return text == null ? null : ("%" + text.toLowerCase() + "%");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String toStringOrNull(final HsOfficeRelationType relationType) {
|
|
||||||
return relationType == null ? null : relationType.name();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ public interface HsOfficeRelationRealRepository extends Repository<HsOfficeRelat
|
|||||||
Optional<HsOfficeRelationRealEntity> findByUuid(UUID id);
|
Optional<HsOfficeRelationRealEntity> findByUuid(UUID id);
|
||||||
|
|
||||||
default List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
|
default List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
|
||||||
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType == null ? null : relationType.toString());
|
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query(value = """
|
@Query(value = """
|
||||||
|
@ -8,6 +8,5 @@ public enum HsOfficeRelationType {
|
|||||||
VIP_CONTACT,
|
VIP_CONTACT,
|
||||||
DEBITOR,
|
DEBITOR,
|
||||||
OPERATIONS,
|
OPERATIONS,
|
||||||
OPERATIONS_ALERT,
|
|
||||||
SUBSCRIBER
|
SUBSCRIBER
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
|||||||
if (entity.getValidity().hasUpperBound()) {
|
if (entity.getValidity().hasUpperBound()) {
|
||||||
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
||||||
}
|
}
|
||||||
resource.getDebitor().setDebitorNumber(entity.getDebitor().getTaggedDebitorNumber());
|
resource.getDebitor().setDebitorNumber(entity.getDebitor().getDebitorNumber());
|
||||||
};
|
};
|
||||||
|
|
||||||
final BiConsumer<HsOfficeSepaMandateInsertResource, HsOfficeSepaMandateEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsOfficeSepaMandateInsertResource, HsOfficeSepaMandateEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
|
@ -7,10 +7,10 @@ import net.hostsharing.hsadminng.errors.DisplayAs;
|
|||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.repr.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@ -30,7 +30,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectRefer
|
|||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(schema = "hs_office", name = "sepamandate_rv")
|
@Table(schema = "hs_office", name = "sepamandate_rv")
|
||||||
|
@ -56,10 +56,6 @@ public class IntegerProperty<P extends IntegerProperty<P>> extends ValidatablePr
|
|||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer min() {
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer max() {
|
public Integer max() {
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void validate(final List<String> result, final String propValue, final PropertiesProvider propProvider) {
|
protected void validate(final List<String> result, final String propValue, final PropertiesProvider propProvider) {
|
||||||
// TODO.legacy: remove after legacy data is migrated
|
// TODO.impl: remove after legacy data is migrated
|
||||||
if (HashGenerator.using(hashedUsing).couldBeHash(propValue) && propValue.length() > this.maxLength()) {
|
if (HashGenerator.using(hashedUsing).couldBeHash(propValue) && propValue.length() > this.maxLength()) {
|
||||||
// already hashed => do not validate
|
// already hashed => do not validate
|
||||||
return;
|
return;
|
||||||
|
@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.validation;
|
|||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.mapper.Array;
|
import net.hostsharing.hsadminng.mapper.Array;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -84,15 +83,11 @@ public class StringProperty<P extends StringProperty<P>> extends ValidatableProp
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// predefined values, similar to fixed values in a combobox
|
/// predefined values, similar to fixed values in a combobox
|
||||||
public P provided(final String firstProvidedValue, final String... moreProvidedValues) {
|
public P provided(final String... provided) {
|
||||||
this.provided = ArrayUtils.addAll(new String[]{firstProvidedValue}, moreProvidedValues);
|
this.provided = provided;
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] provided() {
|
|
||||||
return this.provided;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The property value is not disclosed in error messages.
|
* The property value is not disclosed in error messages.
|
||||||
*
|
*
|
||||||
@ -114,11 +109,7 @@ public class StringProperty<P extends StringProperty<P>> extends ValidatableProp
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String display(final String propValue) {
|
protected String display(final String propValue) {
|
||||||
return undisclosed
|
return undisclosed ? "provided value" : ("'" + propValue + "'");
|
||||||
? "provided value"
|
|
||||||
: propValue != null
|
|
||||||
? ("'" + propValue + "'")
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.lambda;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class Reducer {
|
|
||||||
public static <T> T toSingleElement(T ignoredLast, T ignoredNext) {
|
|
||||||
throw new AssertionError("only a single entity expected");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +1,11 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
import org.modelmapper.ModelMapper;
|
import org.modelmapper.ModelMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.PersistenceContext;
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -21,10 +21,10 @@ import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
|||||||
*/
|
*/
|
||||||
abstract class Mapper extends ModelMapper {
|
abstract class Mapper extends ModelMapper {
|
||||||
|
|
||||||
EntityManagerWrapper em;
|
@PersistenceContext
|
||||||
|
EntityManager em;
|
||||||
|
|
||||||
Mapper(@Autowired final EntityManagerWrapper em) {
|
Mapper() {
|
||||||
this.em = em;
|
|
||||||
getConfiguration().setAmbiguityIgnored(true);
|
getConfiguration().setAmbiguityIgnored(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,8 +8,7 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class StandardMapper extends Mapper {
|
public class StandardMapper extends Mapper {
|
||||||
|
|
||||||
public StandardMapper(@Autowired final EntityManagerWrapper em) {
|
public StandardMapper() {
|
||||||
super(em);
|
|
||||||
getConfiguration().setAmbiguityIgnored(true);
|
getConfiguration().setAmbiguityIgnored(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import static org.modelmapper.convention.MatchingStrategies.STRICT;
|
import static org.modelmapper.convention.MatchingStrategies.STRICT;
|
||||||
@ -15,8 +13,7 @@ import static org.modelmapper.convention.MatchingStrategies.STRICT;
|
|||||||
@Component
|
@Component
|
||||||
public class StrictMapper extends Mapper {
|
public class StrictMapper extends Mapper {
|
||||||
|
|
||||||
public StrictMapper(@Autowired final EntityManagerWrapper em) {
|
public StrictMapper() {
|
||||||
super(em);
|
|
||||||
getConfiguration().setMatchingStrategy(STRICT);
|
getConfiguration().setMatchingStrategy(STRICT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import static java.util.stream.Collectors.joining;
|
|
||||||
|
|
||||||
public class ToStringConverter {
|
|
||||||
|
|
||||||
final public Set<String> ignoredFields = new HashSet<>();
|
|
||||||
|
|
||||||
public ToStringConverter ignoring(final String fieldName) {
|
|
||||||
ignoredFields.add(fieldName);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String from(final Object obj) {
|
|
||||||
return "{ " +
|
|
||||||
Arrays.stream(obj.getClass().getDeclaredFields())
|
|
||||||
.filter(f -> !ignoredFields.contains(f.getName()))
|
|
||||||
.map(field -> {
|
|
||||||
try {
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field.getName() + ": " + field.get(obj);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
// ignore inaccessible fields
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.collect(joining(", "))
|
|
||||||
+ " }";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String from(final Map<?, ?> map) {
|
|
||||||
return "{ "
|
|
||||||
+ map.keySet().stream()
|
|
||||||
.filter(key -> !ignoredFields.contains(key.toString()))
|
|
||||||
.sorted()
|
|
||||||
.map(k -> Map.entry(k, map.get(k)))
|
|
||||||
.map(e -> e.getKey() + ": " + e.getValue())
|
|
||||||
.collect(joining(", "))
|
|
||||||
+ " }";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.persistence;
|
package net.hostsharing.hsadminng.persistence;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
||||||
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.rbac.generator;
|
|||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
import org.reflections.scanners.TypeAnnotationsScanner;
|
import org.reflections.scanners.TypeAnnotationsScanner;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class RbacGrantsDiagramService {
|
|||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
private final Map<UUID, List<RawRbacGrantEntity>> descendantsByUuid = new HashMap<>();
|
private Map<UUID, List<RawRbacGrantEntity>> descendantsByUuid = new HashMap<>();
|
||||||
|
|
||||||
public String allGrantsTocurrentSubject(final EnumSet<Include> includes) {
|
public String allGrantsTocurrentSubject(final EnumSet<Include> includes) {
|
||||||
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||||
@ -231,7 +231,8 @@ public class RbacGrantsDiagramService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record Node(String idName, UUID uuid) {
|
}
|
||||||
|
|
||||||
}
|
record Node(String idName, UUID uuid) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package net.hostsharing.hsadminng.persistence;
|
package net.hostsharing.hsadminng.rbac.object;
|
||||||
|
|
||||||
|
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
// TODO.impl: this class does not really belong into this package, but there is no right place yet
|
||||||
public interface BaseEntity<T extends BaseEntity<?>> {
|
public interface BaseEntity<T extends BaseEntity<?>> {
|
||||||
UUID getUuid();
|
UUID getUuid();
|
||||||
|
|
@ -5,7 +5,7 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.rbac.test.pac.TestPackageEntity;
|
import net.hostsharing.hsadminng.rbac.test.pac.TestPackageEntity;
|
||||||
|
@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||||
import net.hostsharing.hsadminng.rbac.test.cust.TestCustomerEntity;
|
import net.hostsharing.hsadminng.rbac.test.cust.TestCustomerEntity;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.repr;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class TaggedNumber {
|
|
||||||
|
|
||||||
public static Integer cropTag(final String tag, final String taggedNumber) {
|
|
||||||
return taggedNumber.startsWith(tag) ? Integer.valueOf(taggedNumber.substring(tag.length())) : invalidTag(tag, taggedNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer invalidTag(final String tag, final String taggedNumber) {
|
|
||||||
throw new IllegalArgumentException("Expected " + tag + "... but got: " + taggedNumber);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package net.hostsharing.hsadminng.repr;
|
package net.hostsharing.hsadminng.stringify;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package net.hostsharing.hsadminng.repr;
|
package net.hostsharing.hsadminng.stringify;
|
||||||
|
|
||||||
public interface Stringifyable {
|
public interface Stringifyable {
|
||||||
|
|
@ -14,7 +14,6 @@ public class SystemProcess {
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String stdOut;
|
private String stdOut;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String stdErr;
|
private String stdErr;
|
||||||
|
|
||||||
@ -22,6 +21,7 @@ public class SystemProcess {
|
|||||||
this.processBuilder = new ProcessBuilder(command);
|
this.processBuilder = new ProcessBuilder(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getCommand() {
|
public String getCommand() {
|
||||||
return processBuilder.command().toString();
|
return processBuilder.command().toString();
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ components:
|
|||||||
- CLOUD_SERVER
|
- CLOUD_SERVER
|
||||||
- MANAGED_SERVER
|
- MANAGED_SERVER
|
||||||
- MANAGED_WEBSPACE
|
- MANAGED_WEBSPACE
|
||||||
- DOMAIN_SETUP
|
|
||||||
|
|
||||||
HsBookingItem:
|
HsBookingItem:
|
||||||
type: object
|
type: object
|
||||||
@ -52,11 +51,7 @@ components:
|
|||||||
HsBookingItemInsert:
|
HsBookingItemInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
project.uuid:
|
projectUuid:
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
nullable: false
|
|
||||||
parentItem.uuid:
|
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: false
|
nullable: false
|
||||||
@ -73,11 +68,9 @@ components:
|
|||||||
nullable: true
|
nullable: true
|
||||||
resources:
|
resources:
|
||||||
$ref: '#/components/schemas/BookingResources'
|
$ref: '#/components/schemas/BookingResources'
|
||||||
hostingAsset:
|
|
||||||
$ref: '../hs-hosting/hs-hosting-asset-schemas.yaml#/components/schemas/HsHostingAssetAutoInsert'
|
|
||||||
required:
|
required:
|
||||||
- caption
|
- caption
|
||||||
- project.uuid
|
- projectUuid
|
||||||
- validFrom
|
- validFrom
|
||||||
- resources
|
- resources
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
@ -25,7 +25,7 @@ components:
|
|||||||
HsBookingProjectInsert:
|
HsBookingProjectInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
debitor.uuid:
|
debitorUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: false
|
nullable: false
|
||||||
@ -35,6 +35,6 @@ components:
|
|||||||
maxLength: 80
|
maxLength: 80
|
||||||
nullable: false
|
nullable: false
|
||||||
required:
|
required:
|
||||||
- debitor.uuid
|
- debitorUuid
|
||||||
- caption
|
- caption
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
@ -54,7 +54,7 @@ components:
|
|||||||
caption:
|
caption:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
alarmContact.uuid:
|
alarmContactUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
@ -64,11 +64,11 @@ components:
|
|||||||
HsHostingAssetInsert:
|
HsHostingAssetInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
bookingItem.uuid:
|
bookingItemUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
parentAsset.uuid:
|
parentAssetUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
@ -84,7 +84,7 @@ components:
|
|||||||
minLength: 3
|
minLength: 3
|
||||||
maxLength: 80
|
maxLength: 80
|
||||||
nullable: false
|
nullable: false
|
||||||
alarmContact.uuid:
|
alarmContactUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
@ -94,68 +94,7 @@ components:
|
|||||||
- type
|
- type
|
||||||
- identifier
|
- identifier
|
||||||
- caption
|
- caption
|
||||||
additionalProperties: false
|
- config
|
||||||
|
|
||||||
HsHostingAssetAutoInsert:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
parentAsset.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
nullable: true
|
|
||||||
assignedToAsset.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/HsHostingAssetType'
|
|
||||||
identifier:
|
|
||||||
type: string
|
|
||||||
minLength: 3
|
|
||||||
maxLength: 80
|
|
||||||
nullable: false
|
|
||||||
caption:
|
|
||||||
type: string
|
|
||||||
minLength: 3
|
|
||||||
maxLength: 80
|
|
||||||
nullable: false
|
|
||||||
alarmContact.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
nullable: true
|
|
||||||
config:
|
|
||||||
$ref: '#/components/schemas/HsHostingAssetConfiguration'
|
|
||||||
subHostingAssets:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/HsHostingAssetSubInsert'
|
|
||||||
required:
|
|
||||||
- identifier
|
|
||||||
additionalProperties: false
|
|
||||||
|
|
||||||
HsHostingAssetSubInsert:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/HsHostingAssetType'
|
|
||||||
identifier:
|
|
||||||
type: string
|
|
||||||
minLength: 3
|
|
||||||
maxLength: 80
|
|
||||||
nullable: false
|
|
||||||
caption:
|
|
||||||
type: string
|
|
||||||
minLength: 3
|
|
||||||
maxLength: 80
|
|
||||||
nullable: false
|
|
||||||
assignedToAsset.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
alarmContact.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
nullable: true
|
|
||||||
config:
|
|
||||||
$ref: '#/components/schemas/HsHostingAssetConfiguration'
|
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
||||||
HsHostingAssetConfiguration:
|
HsHostingAssetConfiguration:
|
||||||
|
@ -12,7 +12,7 @@ components:
|
|||||||
caption:
|
caption:
|
||||||
type: string
|
type: string
|
||||||
postalAddress:
|
postalAddress:
|
||||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
type: string
|
||||||
emailAddresses:
|
emailAddresses:
|
||||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||||
phoneNumbers:
|
phoneNumbers:
|
||||||
@ -24,7 +24,7 @@ components:
|
|||||||
caption:
|
caption:
|
||||||
type: string
|
type: string
|
||||||
postalAddress:
|
postalAddress:
|
||||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
type: string
|
||||||
emailAddresses:
|
emailAddresses:
|
||||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||||
phoneNumbers:
|
phoneNumbers:
|
||||||
@ -39,48 +39,21 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
postalAddress:
|
postalAddress:
|
||||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
type: string
|
||||||
|
nullable: true
|
||||||
emailAddresses:
|
emailAddresses:
|
||||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||||
phoneNumbers:
|
phoneNumbers:
|
||||||
$ref: '#/components/schemas/HsOfficeContactPhoneNumbers'
|
$ref: '#/components/schemas/HsOfficeContactPhoneNumbers'
|
||||||
|
|
||||||
HsOfficeContactPostalAddress:
|
|
||||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
|
||||||
anyOf:
|
|
||||||
- type: object
|
|
||||||
properties:
|
|
||||||
firm:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
co:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
street:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
zipcode:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
city:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
country:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
additionalProperties: true
|
|
||||||
|
|
||||||
HsOfficeContactEmailAddresses:
|
HsOfficeContactEmailAddresses:
|
||||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
# forces generating a java.lang.Object containing a Map, instead of class HsOfficeContactEmailAddresses
|
||||||
anyOf:
|
anyOf:
|
||||||
- type: object
|
- type: object
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
|
|
||||||
HsOfficeContactPhoneNumbers:
|
HsOfficeContactPhoneNumbers:
|
||||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
# forces generating a java.lang.Object containing a Map, instead of class HsOfficeContactEmailAddresses
|
||||||
anyOf:
|
anyOf:
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -6,7 +6,7 @@ components:
|
|||||||
HsOfficeCoopAssetsTransactionType:
|
HsOfficeCoopAssetsTransactionType:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- REVERSAL
|
- ADJUSTMENT
|
||||||
- DEPOSIT
|
- DEPOSIT
|
||||||
- DISBURSAL
|
- DISBURSAL
|
||||||
- TRANSFER
|
- TRANSFER
|
||||||
@ -32,15 +32,15 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
revertedAssetTx:
|
adjustedAssetTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
||||||
reversalAssetTx:
|
adjustmentAssetTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
||||||
|
|
||||||
HsOfficeReferencedCoopAssetsTransaction:
|
HsOfficeReferencedCoopAssetsTransaction:
|
||||||
description:
|
description:
|
||||||
Similar to `HsOfficeCoopAssetsTransaction` but without the self-referencing properties
|
Similar to `HsOfficeCoopAssetsTransaction` but without the self-referencing properties
|
||||||
(`revertedAssetTx` and `reversalAssetTx`), to avoid recursive JSON.
|
(`adjustedAssetTx` and `adjustmentAssetTx`), to avoid recursive JSON.
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
uuid:
|
uuid:
|
||||||
@ -62,7 +62,7 @@ components:
|
|||||||
HsOfficeCoopAssetsTransactionInsert:
|
HsOfficeCoopAssetsTransactionInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
membership.uuid:
|
membershipUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: false
|
nullable: false
|
||||||
@ -80,11 +80,11 @@ components:
|
|||||||
maxLength: 48
|
maxLength: 48
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
revertedAssetTx.uuid:
|
reverseEntryUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
- membership.uuid
|
- membershipUuid
|
||||||
- transactionType
|
- transactionType
|
||||||
- assetValue
|
- assetValue
|
||||||
- valueDate
|
- valueDate
|
||||||
|
@ -2,7 +2,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- hs-office-coopAssets
|
- hs-office-coopAssets
|
||||||
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
|
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
|
||||||
operationId: getSingleCoopAssetTransactionByUuid
|
operationId: getCoopAssetTransactionByUuid
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -3,7 +3,7 @@ get:
|
|||||||
description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current subject or any of it's assumed roles.
|
description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current subject or any of it's assumed roles.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-coopAssets
|
- hs-office-coopAssets
|
||||||
operationId: getListOfCoopAssets
|
operationId: listCoopAssets
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
@ -46,7 +46,7 @@ post:
|
|||||||
summary: Adds a new cooperative asset transaction.
|
summary: Adds a new cooperative asset transaction.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-coopAssets
|
- hs-office-coopAssets
|
||||||
operationId: postNewCoopAssetTransaction
|
operationId: addCoopAssetsTransaction
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -6,7 +6,7 @@ components:
|
|||||||
HsOfficeCoopSharesTransactionType:
|
HsOfficeCoopSharesTransactionType:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- REVERSAL
|
- ADJUSTMENT
|
||||||
- SUBSCRIPTION
|
- SUBSCRIPTION
|
||||||
- CANCELLATION
|
- CANCELLATION
|
||||||
|
|
||||||
@ -27,15 +27,15 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
revertedShareTx:
|
adjustedShareTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
||||||
reversalShareTx:
|
adjustmentShareTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
||||||
|
|
||||||
HsOfficeReferencedCoopSharesTransaction:
|
HsOfficeReferencedCoopSharesTransaction:
|
||||||
description:
|
description:
|
||||||
Similar to `HsOfficeCoopSharesTransaction` but without the self-referencing properties
|
Similar to `HsOfficeCoopSharesTransaction` but without the self-referencing properties
|
||||||
(`revertedShareTx` and `reversalShareTx`), to avoid recursive JSON.
|
(`adjustedShareTx` and `adjustmentShareTx`), to avoid recursive JSON.
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
uuid:
|
uuid:
|
||||||
@ -56,7 +56,7 @@ components:
|
|||||||
HsOfficeCoopSharesTransactionInsert:
|
HsOfficeCoopSharesTransactionInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
membership.uuid:
|
membershipUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: false
|
nullable: false
|
||||||
@ -73,11 +73,11 @@ components:
|
|||||||
maxLength: 48
|
maxLength: 48
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
revertedShareTx.uuid:
|
adjustedShareTxUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
- membership.uuid
|
- membershipUuid
|
||||||
- transactionType
|
- transactionType
|
||||||
- shareCount
|
- shareCount
|
||||||
- valueDate
|
- valueDate
|
||||||
|
@ -2,7 +2,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- hs-office-coopShares
|
- hs-office-coopShares
|
||||||
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
|
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
|
||||||
operationId: getSingleCoopShareTransactionByUuid
|
operationId: getCoopShareTransactionByUuid
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -3,7 +3,7 @@ get:
|
|||||||
description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current subject or any of it's assumed roles.
|
description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current subject or any of it's assumed roles.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-coopShares
|
- hs-office-coopShares
|
||||||
operationId: getListOfCoopShares
|
operationId: listCoopShares
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
@ -46,7 +46,7 @@ post:
|
|||||||
summary: Adds a new cooperative share transaction.
|
summary: Adds a new cooperative share transaction.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-coopShares
|
- hs-office-coopShares
|
||||||
operationId: postNewCoopSharesTransaction
|
operationId: addCoopSharesTransaction
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -12,13 +12,15 @@ components:
|
|||||||
debitorRel:
|
debitorRel:
|
||||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||||
debitorNumber:
|
debitorNumber:
|
||||||
type: string
|
type: integer
|
||||||
minLength: 9
|
format: int32
|
||||||
maxLength: 9
|
minimum: 1000000
|
||||||
pattern: 'D-[0-9]{7}'
|
maximum: 9999999
|
||||||
debitorNumberSuffix:
|
debitorNumberSuffix:
|
||||||
type: string
|
type: integer
|
||||||
pattern: '^[0-9][0-9]$'
|
format: int8
|
||||||
|
minimum: 00
|
||||||
|
maximum: 99
|
||||||
partner:
|
partner:
|
||||||
$ref: 'hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
|
$ref: 'hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
|
||||||
billable:
|
billable:
|
||||||
@ -41,7 +43,7 @@ components:
|
|||||||
HsOfficeDebitorPatch:
|
HsOfficeDebitorPatch:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
debitorRel.uuid:
|
debitorRelUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
@ -61,7 +63,7 @@ components:
|
|||||||
vatReverseCharge:
|
vatReverseCharge:
|
||||||
type: boolean
|
type: boolean
|
||||||
nullable: false
|
nullable: false
|
||||||
refundBankAccount.uuid:
|
refundBankAccountUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
@ -74,13 +76,15 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
debitorRel:
|
debitorRel:
|
||||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationSubInsert'
|
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
|
||||||
debitorRel.uuid:
|
debitorRelUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
debitorNumberSuffix:
|
debitorNumberSuffix:
|
||||||
type: string
|
type: integer
|
||||||
pattern: '^[0-9][0-9]$'
|
format: int8
|
||||||
|
minimum: 00
|
||||||
|
maximum: 99
|
||||||
billable:
|
billable:
|
||||||
type: boolean
|
type: boolean
|
||||||
vatId:
|
vatId:
|
||||||
@ -92,7 +96,7 @@ components:
|
|||||||
type: boolean
|
type: boolean
|
||||||
vatReverseCharge:
|
vatReverseCharge:
|
||||||
type: boolean
|
type: boolean
|
||||||
refundBankAccount.uuid:
|
refundBankAccountUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
defaultPrefix:
|
defaultPrefix:
|
||||||
|
@ -2,7 +2,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- hs-office-debitors
|
- hs-office-debitors
|
||||||
description: 'Fetch a single debitor by its uuid, if visible for the current subject.'
|
description: 'Fetch a single debitor by its uuid, if visible for the current subject.'
|
||||||
operationId: getSingleDebitorByUuid
|
operationId: getDebitorByUuid
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -3,7 +3,7 @@ get:
|
|||||||
description: Returns the list of (optionally filtered) debitors which are visible to the current subject or any of it's assumed roles.
|
description: Returns the list of (optionally filtered) debitors which are visible to the current subject or any of it's assumed roles.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-debitors
|
- hs-office-debitors
|
||||||
operationId: getListOfDebitors
|
operationId: listDebitors
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
@ -17,10 +17,7 @@ get:
|
|||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: integer
|
||||||
minLength: 9
|
|
||||||
maxLength: 9
|
|
||||||
pattern: 'D-[0-9]{7}'
|
|
||||||
description: Debitor number of the requested debitor.
|
description: Debitor number of the requested debitor.
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -40,7 +37,7 @@ post:
|
|||||||
summary: Adds a new debitor.
|
summary: Adds a new debitor.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-debitors
|
- hs-office-debitors
|
||||||
operationId: postNewDebitor
|
operationId: addDebitor
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -26,10 +26,9 @@ components:
|
|||||||
mainDebitor:
|
mainDebitor:
|
||||||
$ref: 'hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor'
|
$ref: 'hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor'
|
||||||
memberNumber:
|
memberNumber:
|
||||||
type: string
|
type: integer
|
||||||
minLength: 9
|
minimum: 1000000
|
||||||
maxLength: 9
|
maximum: 9999999
|
||||||
pattern: 'M-[0-9]{7}'
|
|
||||||
memberNumberSuffix:
|
memberNumberSuffix:
|
||||||
type: string
|
type: string
|
||||||
minLength: 2
|
minLength: 2
|
||||||
@ -63,7 +62,7 @@ components:
|
|||||||
HsOfficeMembershipInsert:
|
HsOfficeMembershipInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
partner.uuid:
|
partnerUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: false
|
nullable: false
|
||||||
@ -87,7 +86,7 @@ components:
|
|||||||
nullable: false
|
nullable: false
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- partner.uuid
|
- partnerUuid
|
||||||
- memberNumberSuffix
|
- memberNumberSuffix
|
||||||
- validFrom
|
- validFrom
|
||||||
- membershipFeeBillable
|
- membershipFeeBillable
|
||||||
|
@ -2,7 +2,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- hs-office-memberships
|
- hs-office-memberships
|
||||||
description: 'Fetch a single membership by its uuid, if visible for the current subject.'
|
description: 'Fetch a single membership by its uuid, if visible for the current subject.'
|
||||||
operationId: getSingleMembershipByUuid
|
operationId: getMembershipByUuid
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -4,7 +4,7 @@ get:
|
|||||||
The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time.
|
The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-memberships
|
- hs-office-memberships
|
||||||
operationId: getListOfMemberships
|
operationId: listMemberships
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
@ -19,10 +19,7 @@ get:
|
|||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: integer
|
||||||
minLength: 9
|
|
||||||
maxLength: 9
|
|
||||||
pattern: 'M-[0-9]{7}'
|
|
||||||
description: Member number, exclusive to `partnerUuid`.
|
description: Member number, exclusive to `partnerUuid`.
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -42,7 +39,7 @@ post:
|
|||||||
summary: Adds a new membership.
|
summary: Adds a new membership.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-memberships
|
- hs-office-memberships
|
||||||
operationId: postNewMembership
|
operationId: addMembership
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -10,10 +10,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
partnerNumber:
|
partnerNumber:
|
||||||
type: string
|
type: integer
|
||||||
minLength: 7
|
format: int8
|
||||||
maxLength: 7
|
minimum: 10000
|
||||||
pattern: 'P-[0-9]{5}'
|
maximum: 99999
|
||||||
partnerRel:
|
partnerRel:
|
||||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||||
details:
|
details:
|
||||||
@ -50,7 +50,7 @@ components:
|
|||||||
HsOfficePartnerPatch:
|
HsOfficePartnerPatch:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
partnerRel.uuid:
|
partnerRelUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
@ -86,10 +86,10 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
partnerNumber:
|
partnerNumber:
|
||||||
type: string
|
type: integer
|
||||||
minLength: 7
|
format: int8
|
||||||
maxLength: 7
|
minimum: 10000
|
||||||
pattern: 'P-[0-9]{5}'
|
maximum: 99999
|
||||||
partnerRel:
|
partnerRel:
|
||||||
$ref: '#/components/schemas/HsOfficePartnerRelInsert'
|
$ref: '#/components/schemas/HsOfficePartnerRelInsert'
|
||||||
details:
|
details:
|
||||||
@ -103,19 +103,19 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
nullable: false
|
nullable: false
|
||||||
properties:
|
properties:
|
||||||
anchor.uuid:
|
anchorUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
holder.uuid:
|
holderUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
contact.uuid:
|
contactUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
- anchor.uuid
|
- anchorUuid
|
||||||
- holder.uuid
|
- holderUuid
|
||||||
- relContact.uuid
|
- relContactUuid
|
||||||
|
|
||||||
HsOfficePartnerDetailsInsert:
|
HsOfficePartnerDetailsInsert:
|
||||||
type: object
|
type: object
|
||||||
|
@ -13,7 +13,6 @@ components:
|
|||||||
- REPRESENTATIVE
|
- REPRESENTATIVE
|
||||||
- VIP_CONTACT
|
- VIP_CONTACT
|
||||||
- OPERATIONS
|
- OPERATIONS
|
||||||
- OPERATIONS_ALERT
|
|
||||||
- SUBSCRIBER
|
- SUBSCRIBER
|
||||||
|
|
||||||
HsOfficeRelation:
|
HsOfficeRelation:
|
||||||
@ -37,19 +36,18 @@ components:
|
|||||||
HsOfficeRelationPatch:
|
HsOfficeRelationPatch:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
contact.uuid:
|
contactUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
|
|
||||||
# arbitrary relation with explicit type
|
|
||||||
HsOfficeRelationInsert:
|
HsOfficeRelationInsert:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
anchor.uuid:
|
anchorUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
holder.uuid:
|
holderUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
type:
|
type:
|
||||||
@ -58,32 +56,11 @@ components:
|
|||||||
mark:
|
mark:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
contact.uuid:
|
contactUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
- anchor.uuid
|
- anchorUuid
|
||||||
- holder.uuid
|
- holderUuid
|
||||||
- type
|
- type
|
||||||
- contact.uuid
|
- contactUuid
|
||||||
|
|
||||||
# relation created as a sub-element with implicitly known type
|
|
||||||
HsOfficeRelationSubInsert:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
anchor.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
holder.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
mark:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
contact.uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
required:
|
|
||||||
- anchor.uuid
|
|
||||||
- holder.uuid
|
|
||||||
- contact.uuid
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
get:
|
get:
|
||||||
summary: Returns a list of (optionally filtered) person relations for a given person.
|
summary: Returns a list of (optionally filtered) person relations for a given person.
|
||||||
description:
|
description: Returns the list of (optionally filtered) person relations of a given person and which are visible to the current subject or any of it's assumed roles.
|
||||||
Returns the list of (optionally filtered) person relations of a given person and which are visible to the current subject or any of it's assumed roles.
|
|
||||||
To match data, all given query parameters must be fulfilled ('and' / logical conjunction).
|
|
||||||
tags:
|
tags:
|
||||||
- hs-office-relations
|
- hs-office-relations
|
||||||
operationId: listRelations
|
operationId: listRelations
|
||||||
@ -11,7 +9,7 @@ get:
|
|||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
- name: personUuid
|
- name: personUuid
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
@ -22,18 +20,6 @@ get:
|
|||||||
schema:
|
schema:
|
||||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationType'
|
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationType'
|
||||||
description: Prefix of name properties from holder or contact to filter the results.
|
description: Prefix of name properties from holder or contact to filter the results.
|
||||||
- name: personData
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: 'Data from any of these text field in the anchor or holder person: tradeName, familyName, givenName'
|
|
||||||
- name: contactData
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: 'Data from any of these text field in the contact: caption, postalAddress, emailAddresses, phoneNumbers'
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user