Merge branch 'master' into TP-20240927-importfixes
This commit is contained in:
commit
070e63c7b8
36
Jenkinsfile
vendored
36
Jenkinsfile
vendored
@ -26,9 +26,35 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Compile & Test') {
|
||||
stage ('Compile') {
|
||||
steps {
|
||||
sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze'
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,6 +71,12 @@ pipeline {
|
||||
sourcePattern: 'src/main/java'
|
||||
)
|
||||
|
||||
// archive scenario-test reports in HTML format
|
||||
sh '''
|
||||
./gradlew convertMarkdownToHtml
|
||||
'''
|
||||
archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true
|
||||
|
||||
// cleanup workspace
|
||||
cleanWs()
|
||||
}
|
||||
|
55
build.gradle
55
build.gradle
@ -255,7 +255,7 @@ test {
|
||||
'net.hostsharing.hsadminng.**.generated.**',
|
||||
]
|
||||
useJUnitPlatform {
|
||||
excludeTags 'import'
|
||||
excludeTags 'importOfficeData', 'importHostingData', 'scenarioTest'
|
||||
}
|
||||
}
|
||||
jacocoTestReport {
|
||||
@ -344,6 +344,17 @@ tasks.register('importHostingAssets', Test) {
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
tasks.register('scenarioTests', Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags 'scenarioTest'
|
||||
}
|
||||
|
||||
group 'verification'
|
||||
description 'run the import jobs as tests'
|
||||
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
// pitest mutation testing
|
||||
pitest {
|
||||
targetClasses = ['net.hostsharing.hsadminng.**']
|
||||
@ -391,3 +402,45 @@ tasks.named("dependencyUpdates").configure {
|
||||
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: '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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
124
doc/scenarios/template.html
Normal file
124
doc/scenarios/template.html
Normal file
@ -0,0 +1,124 @@
|
||||
<!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>
|
@ -90,6 +90,20 @@ Acceptance-tests, are blackbox-tests and do <u>not</u> count into test-code-cove
|
||||
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-critical scenarios have to be identified and a special performance-test has to be implemented.
|
||||
|
@ -1,10 +1,6 @@
|
||||
FROM eclipse-temurin:21-jdk
|
||||
RUN apt-get update && \
|
||||
apt-get install -y bind9-utils && \
|
||||
apt-get install -y bind9-utils pandoc && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# RUN mkdir /opt/app
|
||||
# COPY japp.jar /opt
|
||||
# CMD ["java", "-jar", "/opt/app/japp.jar"]
|
||||
|
@ -54,8 +54,14 @@ public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContac
|
||||
@Column(name = "caption")
|
||||
private String caption;
|
||||
|
||||
@Builder.Default
|
||||
@Setter(AccessLevel.NONE)
|
||||
@Type(JsonType.class)
|
||||
@Column(name = "postaladdress")
|
||||
private String postalAddress; // multiline free-format text
|
||||
private Map<String, String> postalAddress = new HashMap<>();
|
||||
|
||||
@Transient
|
||||
private PatchableMapWrapper<String> postalAddressWrapper;
|
||||
|
||||
@Builder.Default
|
||||
@Setter(AccessLevel.NONE)
|
||||
@ -75,6 +81,17 @@ public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContac
|
||||
@Transient
|
||||
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() {
|
||||
return PatchableMapWrapper.of(
|
||||
emailAddressesWrapper,
|
||||
|
@ -18,7 +18,8 @@ class HsOfficeContactEntityPatcher implements EntityPatcher<HsOfficeContactPatch
|
||||
@Override
|
||||
public void apply(final HsOfficeContactPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getCaption()).ifPresent(entity::setCaption);
|
||||
OptionalFromJson.of(resource.getPostalAddress()).ifPresent(entity::setPostalAddress);
|
||||
Optional.ofNullable(resource.getPostalAddress())
|
||||
.ifPresent(r -> entity.getPostalAddress().patch(KeyValueMap.from(resource.getPostalAddress())));
|
||||
Optional.ofNullable(resource.getEmailAddresses())
|
||||
.ifPresent(r -> entity.getEmailAddresses().patch(KeyValueMap.from(resource.getEmailAddresses())));
|
||||
Optional.ofNullable(resource.getPhoneNumbers())
|
||||
|
@ -77,16 +77,13 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
|
||||
Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
|
||||
"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,
|
||||
"ERROR: [400] debitorRel.mark must be null");
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
|
||||
if ( body.getDebitorRel() != null ) {
|
||||
body.getDebitorRel().setType(DEBITOR.name());
|
||||
if (body.getDebitorRel() != null) {
|
||||
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
||||
debitorRel.setType(DEBITOR);
|
||||
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
||||
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
||||
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
||||
@ -95,7 +92,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
||||
debitorRelOptional.ifPresentOrElse(
|
||||
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
|
||||
() -> { throw new ValidationException("Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());});
|
||||
() -> {
|
||||
throw new ValidationException(
|
||||
"Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());
|
||||
});
|
||||
}
|
||||
|
||||
final var savedEntity = debitorRepo.save(entityToSave);
|
||||
|
@ -47,7 +47,7 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
|
||||
OR lower(rel.anchor.givenName) LIKE :personData OR lower(rel.holder.givenName) LIKE :personData )
|
||||
AND ( :contactData IS NULL
|
||||
OR lower(rel.contact.caption) LIKE :contactData
|
||||
OR lower(rel.contact.postalAddress) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.postalAddress AS String)) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.emailAddresses AS String)) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.phoneNumbers AS String)) LIKE :contactData )
|
||||
""")
|
||||
|
@ -12,7 +12,7 @@ components:
|
||||
caption:
|
||||
type: string
|
||||
postalAddress:
|
||||
type: string
|
||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
||||
emailAddresses:
|
||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||
phoneNumbers:
|
||||
@ -24,7 +24,7 @@ components:
|
||||
caption:
|
||||
type: string
|
||||
postalAddress:
|
||||
type: string
|
||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
||||
emailAddresses:
|
||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||
phoneNumbers:
|
||||
@ -39,21 +39,48 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
postalAddress:
|
||||
type: string
|
||||
nullable: true
|
||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
||||
emailAddresses:
|
||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||
phoneNumbers:
|
||||
$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:
|
||||
# forces generating a java.lang.Object containing a Map, instead of class HsOfficeContactEmailAddresses
|
||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
||||
anyOf:
|
||||
- type: object
|
||||
additionalProperties: true
|
||||
|
||||
HsOfficeContactPhoneNumbers:
|
||||
# forces generating a java.lang.Object containing a Map, instead of class HsOfficeContactEmailAddresses
|
||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
|
@ -17,10 +17,8 @@ components:
|
||||
minimum: 1000000
|
||||
maximum: 9999999
|
||||
debitorNumberSuffix:
|
||||
type: integer
|
||||
format: int8
|
||||
minimum: 00
|
||||
maximum: 99
|
||||
type: string
|
||||
pattern: '^[0-9][0-9]$'
|
||||
partner:
|
||||
$ref: 'hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
|
||||
billable:
|
||||
@ -76,15 +74,13 @@ components:
|
||||
type: object
|
||||
properties:
|
||||
debitorRel:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationSubInsert'
|
||||
debitorRelUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
debitorNumberSuffix:
|
||||
type: integer
|
||||
format: int8
|
||||
minimum: 00
|
||||
maximum: 99
|
||||
type: string
|
||||
pattern: '^[0-9][0-9]$'
|
||||
billable:
|
||||
type: boolean
|
||||
vatId:
|
||||
|
@ -42,6 +42,7 @@ components:
|
||||
format: uuid
|
||||
nullable: true
|
||||
|
||||
# arbitrary relation with explicit type
|
||||
HsOfficeRelationInsert:
|
||||
type: object
|
||||
properties:
|
||||
@ -65,3 +66,24 @@ components:
|
||||
- holderUuid
|
||||
- type
|
||||
- contactUuid
|
||||
|
||||
# relation created as a sub-element with implicitly known type
|
||||
HsOfficeRelationSubInsert:
|
||||
type: object
|
||||
properties:
|
||||
anchorUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
holderUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
mark:
|
||||
type: string
|
||||
nullable: true
|
||||
contactUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- anchorUuid
|
||||
- holderUuid
|
||||
- contactUuid
|
||||
|
@ -7,7 +7,7 @@ get:
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: name
|
||||
- name: iban
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
@ -102,10 +102,10 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.customer WHERE uuid = OLD.customerUuid INTO oldCustomer;
|
||||
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
|
||||
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s of package', OLD.customerUuid);
|
||||
|
||||
SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid);
|
||||
|
||||
|
||||
if NEW.customerUuid <> OLD.customerUuid then
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
@ -98,10 +98,10 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.package WHERE uuid = OLD.packageUuid INTO oldPackage;
|
||||
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
|
||||
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s of domain', OLD.packageUuid);
|
||||
|
||||
SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid);
|
||||
|
||||
|
||||
if NEW.packageUuid <> OLD.packageUuid then
|
||||
|
@ -9,7 +9,7 @@ create table if not exists hs_office.contact
|
||||
uuid uuid unique references rbac.object (uuid) initially deferred,
|
||||
version int not null default 0,
|
||||
caption varchar(128) not null,
|
||||
postalAddress text,
|
||||
postalAddress jsonb not null,
|
||||
emailAddresses jsonb not null,
|
||||
phoneNumbers jsonb not null
|
||||
);
|
||||
|
@ -11,7 +11,6 @@
|
||||
create or replace procedure hs_office.contact_create_test_data(contCaption varchar)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
postalAddr varchar;
|
||||
emailAddr varchar;
|
||||
begin
|
||||
emailAddr = 'contact-admin@' || base.cleanIdentifier(contCaption) || '.example.com';
|
||||
@ -19,14 +18,18 @@ begin
|
||||
perform rbac.create_subject(emailAddr);
|
||||
call base.defineContext('creating contact test-data', null, emailAddr);
|
||||
|
||||
postalAddr := E'Vorname Nachname\nStraße Hnr\nPLZ Stadt';
|
||||
|
||||
raise notice 'creating test contact: %', contCaption;
|
||||
insert
|
||||
into hs_office.contact (caption, postaladdress, emailaddresses, phonenumbers)
|
||||
values (
|
||||
contCaption,
|
||||
postalAddr,
|
||||
( '{ ' ||
|
||||
-- '"name": "' || contCaption || '",' ||
|
||||
-- '"street": "Somewhere 1",' ||
|
||||
-- '"zipcode": "12345",' ||
|
||||
-- '"city": "Where-Ever",' ||
|
||||
'"country": "Germany"' ||
|
||||
'}')::jsonb,
|
||||
('{ "main": "' || emailAddr || '" }')::jsonb,
|
||||
('{ "phone_office": "+49 123 1234567" }')::jsonb
|
||||
);
|
||||
|
@ -38,13 +38,13 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.person WHERE uuid = NEW.holderUuid INTO newHolderPerson;
|
||||
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
|
||||
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s of relation', NEW.holderUuid);
|
||||
|
||||
SELECT * FROM hs_office.person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
|
||||
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
|
||||
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s of relation', NEW.anchorUuid);
|
||||
|
||||
SELECT * FROM hs_office.contact WHERE uuid = NEW.contactUuid INTO newContact;
|
||||
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
|
||||
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s of relation', NEW.contactUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -37,10 +37,10 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid);
|
||||
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'DELETE'), hs_office.relation_OWNER(newPartnerRel));
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.relation_TENANT(newPartnerRel));
|
||||
@ -96,16 +96,16 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel;
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid);
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s of partner', OLD.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails;
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid);
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s of partner', OLD.detailsUuid);
|
||||
|
||||
SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid);
|
||||
|
||||
|
||||
if NEW.partnerRelUuid <> OLD.partnerRelUuid then
|
||||
|
@ -44,10 +44,10 @@ begin
|
||||
WHERE partnerRel.type = 'PARTNER'
|
||||
AND NEW.debitorRelUuid = debitorRel.uuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
|
||||
|
||||
|
@ -37,14 +37,14 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount;
|
||||
assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s', NEW.bankAccountUuid);
|
||||
assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s of sepamandate', NEW.bankAccountUuid);
|
||||
|
||||
SELECT debitorRel.*
|
||||
FROM hs_office.relation debitorRel
|
||||
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = NEW.debitorUuid
|
||||
INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s of sepamandate', NEW.debitorUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -40,7 +40,7 @@ begin
|
||||
JOIN hs_office.relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
|
||||
WHERE partner.uuid = NEW.partnerUuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s of membership', NEW.partnerUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.membership WHERE uuid = NEW.membershipUuid INTO newMembership;
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of coopshares', NEW.membershipUuid);
|
||||
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership));
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership));
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.membership WHERE uuid = NEW.membershipUuid INTO newMembership;
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of coopasset', NEW.membershipUuid);
|
||||
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership));
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership));
|
||||
|
@ -37,14 +37,14 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.debitor WHERE uuid = NEW.debitorUuid INTO newDebitor;
|
||||
assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s of project', NEW.debitorUuid);
|
||||
|
||||
SELECT debitorRel.*
|
||||
FROM hs_office.relation debitorRel
|
||||
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = NEW.debitorUuid
|
||||
INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s or project', NEW.debitorUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -46,6 +46,7 @@ public class ArchitectureTest {
|
||||
"..lambda",
|
||||
"..generated..",
|
||||
"..persistence..",
|
||||
"..reflection",
|
||||
"..system..",
|
||||
"..validation..",
|
||||
"..hs.office.bankaccount",
|
||||
@ -54,6 +55,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.coopshares",
|
||||
"..hs.office.debitor",
|
||||
"..hs.office.membership",
|
||||
"..hs.office.scenarios..",
|
||||
"..hs.migration",
|
||||
"..hs.office.partner",
|
||||
"..hs.office.person",
|
||||
@ -96,7 +98,7 @@ public class ArchitectureTest {
|
||||
public static final ArchRule testClassesAreProperlyNamed = classes()
|
||||
.that().haveSimpleNameEndingWith("Test")
|
||||
.and().doNotHaveModifier(ABSTRACT)
|
||||
.should().haveNameMatching(".*(UnitTest|RestTest|IntegrationTest|AcceptanceTest|ArchitectureTest)$");
|
||||
.should().haveNameMatching(".*(UnitTest|RestTest|IntegrationTest|AcceptanceTest|ScenarioTest|ArchitectureTest)$");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -110,7 +110,6 @@ public class HsHostingAssetControllerRestTest {
|
||||
"caption": "some fake cloud-server",
|
||||
"alarmContact": {
|
||||
"caption": "some contact",
|
||||
"postalAddress": "address of some contact",
|
||||
"emailAddresses": {
|
||||
"main": "some-contact@example.com"
|
||||
}
|
||||
@ -141,7 +140,6 @@ public class HsHostingAssetControllerRestTest {
|
||||
"caption": "some fake managed-server",
|
||||
"alarmContact": {
|
||||
"caption": "some contact",
|
||||
"postalAddress": "address of some contact",
|
||||
"emailAddresses": {
|
||||
"main": "some-contact@example.com"
|
||||
}
|
||||
|
@ -1161,7 +1161,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
contactRecord.getString("last_name"),
|
||||
contactRecord.getString("firma")));
|
||||
contact.putEmailAddresses(Map.of("main", contactRecord.getString("email")));
|
||||
contact.setPostalAddress(toAddress(contactRecord));
|
||||
contact.putPostalAddress(toAddress(contactRecord));
|
||||
contact.putPhoneNumbers(toPhoneNumbers(contactRecord));
|
||||
|
||||
contacts.put(contactRecord.getInteger("contact_id"), contact);
|
||||
@ -1181,36 +1181,23 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
private String toAddress(final Record rec) {
|
||||
final var result = new StringBuilder();
|
||||
private Map<String, String> toAddress(final Record rec) {
|
||||
final var result = new LinkedHashMap<String, String>();
|
||||
final var name = toName(
|
||||
rec.getString("salut"),
|
||||
rec.getString("title"),
|
||||
rec.getString("first_name"),
|
||||
rec.getString("last_name"));
|
||||
if (isNotBlank(name))
|
||||
result.append(name + "\n");
|
||||
result.put("name", name);
|
||||
if (isNotBlank(rec.getString("firma")))
|
||||
result.append(rec.getString("firma") + "\n");
|
||||
if (isNotBlank(rec.getString("co")))
|
||||
result.append("c/o " + rec.getString("co") + "\n");
|
||||
if (isNotBlank(rec.getString("street")))
|
||||
result.append(rec.getString("street") + "\n");
|
||||
final var zipcodeAndCity = toZipcodeAndCity(rec);
|
||||
if (isNotBlank(zipcodeAndCity))
|
||||
result.append(zipcodeAndCity + "\n");
|
||||
return result.toString();
|
||||
}
|
||||
result.put("firm", name);
|
||||
|
||||
private String toZipcodeAndCity(final Record rec) {
|
||||
final var result = new StringBuilder();
|
||||
if (isNotBlank(rec.getString("country")))
|
||||
result.append(rec.getString("country") + " ");
|
||||
if (isNotBlank(rec.getString("zipcode")))
|
||||
result.append(rec.getString("zipcode") + " ");
|
||||
if (isNotBlank(rec.getString("city")))
|
||||
result.append(rec.getString("city"));
|
||||
return result.toString();
|
||||
List.of("co", "street", "zipcode", "city", "country").forEach(key -> {
|
||||
if (isNotBlank(rec.getString(key)))
|
||||
result.put(key, rec.getString(key));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private String toCaption(
|
||||
|
@ -21,10 +21,13 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid;
|
||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@ -214,7 +217,11 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
"emailAddresses": {
|
||||
"main": "patched@example.org"
|
||||
},
|
||||
"postalAddress": "Patched Address",
|
||||
"postalAddress": {
|
||||
"extra": "Extra Property",
|
||||
"co": "P. Patcher",
|
||||
"street": "Patchstraße 5"
|
||||
},
|
||||
"phoneNumbers": {
|
||||
"phone_office": "+01 100 123456"
|
||||
}
|
||||
@ -229,7 +236,10 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.body("uuid", isUuidValid())
|
||||
.body("caption", is("Temp patched contact"))
|
||||
.body("emailAddresses", is(Map.of("main", "patched@example.org")))
|
||||
.body("postalAddress", is("Patched Address"))
|
||||
.body("postalAddress", hasEntry("name", givenContact.getPostalAddress().get("name"))) // unchanged
|
||||
.body("postalAddress", hasEntry("extra", "Extra Property")) // unchanged
|
||||
.body("postalAddress", hasEntry("co", "P. Patcher")) // patched
|
||||
.body("postalAddress", hasEntry("street", "Patchstraße 5")) // patched
|
||||
.body("phoneNumbers", is(Map.of("phone_office", "+01 100 123456")));
|
||||
// @formatter:on
|
||||
|
||||
@ -239,7 +249,11 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.matches(person -> {
|
||||
assertThat(person.getCaption()).isEqualTo("Temp patched contact");
|
||||
assertThat(person.getEmailAddresses()).containsExactlyEntriesOf(Map.of("main", "patched@example.org"));
|
||||
assertThat(person.getPostalAddress()).isEqualTo("Patched Address");
|
||||
assertThat(person.getPostalAddress()).containsAllEntriesOf(Map.ofEntries(
|
||||
entry("name", givenContact.getPostalAddress().get("name")),
|
||||
entry("co", "P. Patcher"),
|
||||
entry("street", "Patchstraße 5")
|
||||
));
|
||||
assertThat(person.getPhoneNumbers()).containsExactlyEntriesOf(Map.of("phone_office", "+01 100 123456"));
|
||||
return true;
|
||||
});
|
||||
@ -264,7 +278,7 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
"phone_office": "+01 100 123456"
|
||||
}
|
||||
}
|
||||
""")
|
||||
""")
|
||||
.port(port)
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/office/contacts/" + givenContact.getUuid())
|
||||
@ -274,7 +288,6 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.body("uuid", isUuidValid())
|
||||
.body("caption", is(givenContact.getCaption()))
|
||||
.body("emailAddresses", is(Map.of("main", "patched@example.org")))
|
||||
.body("postalAddress", is(givenContact.getPostalAddress()))
|
||||
.body("phoneNumbers", is(Map.of("phone_office", "+01 100 123456")));
|
||||
// @formatter:on
|
||||
|
||||
@ -283,12 +296,11 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.matches(person -> {
|
||||
assertThat(person.getCaption()).isEqualTo(givenContact.getCaption());
|
||||
assertThat(person.getEmailAddresses()).containsExactlyEntriesOf(Map.of("main", "patched@example.org"));
|
||||
assertThat(person.getPostalAddress()).isEqualTo(givenContact.getPostalAddress());
|
||||
assertThat(person.getPostalAddress()).containsExactlyInAnyOrderEntriesOf(givenContact.getPostalAddress());
|
||||
assertThat(person.getPhoneNumbers()).containsExactlyEntriesOf(Map.of("phone_office", "+01 100 123456"));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@ -361,8 +373,13 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
final var newContact = HsOfficeContactRbacEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.caption("Temp from " + Context.getCallerMethodNameFromStackFrame(1) )
|
||||
.postalAddress(Map.ofEntries(
|
||||
entry("name", RandomStringUtils.randomAlphabetic(6) + " " + RandomStringUtils.randomAlphabetic(10)),
|
||||
entry("street", RandomStringUtils.randomAlphabetic(10) + randomInt(1, 99)),
|
||||
entry("zipcode", "D-" + randomInt(10000, 99999)),
|
||||
entry("city", RandomStringUtils.randomAlphabetic(10))
|
||||
))
|
||||
.emailAddresses(Map.of("main", RandomStringUtils.randomAlphabetic(10) + "@example.org"))
|
||||
.postalAddress("Postal Address " + RandomStringUtils.randomAlphabetic(10))
|
||||
.phoneNumbers(Map.of("phone_office", "+01 200 " + RandomStringUtils.randomNumeric(8)))
|
||||
.build();
|
||||
|
||||
@ -378,4 +395,8 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
em.createQuery("DELETE FROM HsOfficeContactRbacEntity c WHERE c.caption LIKE 'Temp %'").executeUpdate();
|
||||
}).assertSuccessful();
|
||||
}
|
||||
|
||||
private int randomInt(final int min, final int max) {
|
||||
return ThreadLocalRandom.current().nextInt(min, max);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,19 @@ class HsOfficeContactPatcherUnitTest extends PatchUnitTestBase<
|
||||
> {
|
||||
|
||||
private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID();
|
||||
|
||||
private static final Map<String, String> PATCH_POSTAL_ADDRESS = patchMap(
|
||||
entry("name", "Patty Patch"),
|
||||
entry("street", "Patchstreet 10"),
|
||||
entry("zipcode", null),
|
||||
entry("city", "Hamburg")
|
||||
);
|
||||
private static final Map<String, String> PATCHED_POSTAL_ADDRESS = patchMap(
|
||||
entry("name", "Patty Patch"),
|
||||
entry("street", "Patchstreet 10"),
|
||||
entry("city", "Hamburg")
|
||||
);
|
||||
|
||||
private static final Map<String, String> PATCH_EMAIL_ADDRESSES = patchMap(
|
||||
entry("main", "patched@example.com"),
|
||||
entry("paul", null),
|
||||
@ -46,6 +59,11 @@ class HsOfficeContactPatcherUnitTest extends PatchUnitTestBase<
|
||||
final var entity = new HsOfficeContactRbacEntity();
|
||||
entity.setUuid(INITIAL_CONTACT_UUID);
|
||||
entity.setCaption("initial caption");
|
||||
entity.putPostalAddress(Map.ofEntries(
|
||||
entry("name", "Ina Initial"),
|
||||
entry("street", "Initialstraße 50"),
|
||||
entry("zipcode", "20000"),
|
||||
entry("city", "Hamburg")));
|
||||
entity.putEmailAddresses(Map.ofEntries(
|
||||
entry("main", "initial@example.org"),
|
||||
entry("paul", "paul@example.com"),
|
||||
@ -54,7 +72,6 @@ class HsOfficeContactPatcherUnitTest extends PatchUnitTestBase<
|
||||
entry("phone_office", "+49 40 12345-00"),
|
||||
entry("phone_mobile", "+49 1555 1234567"),
|
||||
entry("fax", "+49 40 12345-90")));
|
||||
entity.setPostalAddress("Initialstraße 50\n20000 Hamburg");
|
||||
return entity;
|
||||
}
|
||||
|
||||
@ -77,24 +94,26 @@ class HsOfficeContactPatcherUnitTest extends PatchUnitTestBase<
|
||||
"patched caption",
|
||||
HsOfficeContactRbacEntity::setCaption),
|
||||
new SimpleProperty<>(
|
||||
"resources",
|
||||
"postalAddress",
|
||||
HsOfficeContactPatchResource::setPostalAddress,
|
||||
PATCH_POSTAL_ADDRESS,
|
||||
HsOfficeContactRbacEntity::putPostalAddress,
|
||||
PATCHED_POSTAL_ADDRESS)
|
||||
.notNullable(),
|
||||
new SimpleProperty<>(
|
||||
"emailAddresses",
|
||||
HsOfficeContactPatchResource::setEmailAddresses,
|
||||
PATCH_EMAIL_ADDRESSES,
|
||||
HsOfficeContactRbacEntity::putEmailAddresses,
|
||||
PATCHED_EMAIL_ADDRESSES)
|
||||
.notNullable(),
|
||||
new SimpleProperty<>(
|
||||
"resources",
|
||||
"phoneNumbers",
|
||||
HsOfficeContactPatchResource::setPhoneNumbers,
|
||||
PATCH_PHONE_NUMBERS,
|
||||
HsOfficeContactRbacEntity::putPhoneNumbers,
|
||||
PATCHED_PHONE_NUMBERS)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"patched given name",
|
||||
HsOfficeContactPatchResource::setPostalAddress,
|
||||
"patched given name",
|
||||
HsOfficeContactRbacEntity::setPostalAddress)
|
||||
.notNullable()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package net.hostsharing.hsadminng.hs.office.contact;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
|
||||
public class HsOfficeContactRbacTestEntity {
|
||||
|
||||
public static final HsOfficeContactRbacEntity TEST_RBAC_CONTACT = hsOfficeContact("some contact", "some-contact@example.com");
|
||||
@ -9,7 +11,12 @@ public class HsOfficeContactRbacTestEntity {
|
||||
static public HsOfficeContactRbacEntity hsOfficeContact(final String caption, final String emailAddr) {
|
||||
return HsOfficeContactRbacEntity.builder()
|
||||
.caption(caption)
|
||||
.postalAddress("address of " + caption)
|
||||
.postalAddress(Map.ofEntries(
|
||||
entry("name", "M. Meyer"),
|
||||
entry("street", "Teststraße 11"),
|
||||
entry("zipcode", "D-12345"),
|
||||
entry("city", "Berlin")
|
||||
))
|
||||
.emailAddresses(Map.of("main", emailAddr))
|
||||
.build();
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package net.hostsharing.hsadminng.hs.office.contact;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
|
||||
public class HsOfficeContactRealTestEntity {
|
||||
|
||||
public static final HsOfficeContactRealEntity TEST_REAL_CONTACT = hsOfficeContact("some contact", "some-contact@example.com");
|
||||
@ -9,7 +11,12 @@ public class HsOfficeContactRealTestEntity {
|
||||
static public HsOfficeContactRealEntity hsOfficeContact(final String caption, final String emailAddr) {
|
||||