feature/add-jenkinsfile #114
51
Jenkinsfile
vendored
Normal file
51
Jenkinsfile
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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 & Test') {
|
||||||
|
steps {
|
||||||
|
sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
// archive test results
|
||||||
|
junit 'build/test-results/test/*.xml'
|
||||||
|
|
||||||
|
// archive the JaCoCo coverage report in XML and HTML format
|
||||||
|
jacoco(execPattern: '**/jacoco.exec',
|
||||||
|
classPattern: 'build/classes/java/main',
|
||||||
|
sourcePattern: 'src/main/java',
|
||||||
|
exclusionPattern: '')
|
||||||
|
|
||||||
|
// cleanup workspace
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
etc/jenkinsAgent.Dockerfile
Normal file
10
etc/jenkinsAgent.Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM eclipse-temurin:21-jdk
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y bind9-utils && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
|
# RUN mkdir /opt/app
|
||||||
|
# COPY japp.jar /opt
|
||||||
|
# CMD ["java", "-jar", "/opt/app/japp.jar"]
|
@ -37,7 +37,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
private HsOfficePersonRepository holderRepo;
|
private HsOfficePersonRepository holderRepo;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeContactRealRepository contactrealRepo;
|
private HsOfficeContactRealRepository realContactRepo;
|
||||||
|
|
||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
@ -47,12 +47,20 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
public ResponseEntity<List<HsOfficeRelationResource>> listRelations(
|
public ResponseEntity<List<HsOfficeRelationResource>> listRelations(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID personUuid,
|
UUID personUuid,
|
||||||
final HsOfficeRelationTypeResource relationType) {
|
HsOfficeRelationTypeResource relationType,
|
||||||
|
String personData,
|
||||||
|
String contactData) {
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final var entities = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
|
final var entities =
|
||||||
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()));
|
( personData == null && contactData == null )
|
||||||
|
? relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
|
||||||
|
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()))
|
||||||
|
: relationRbacRepo.findRelationRelatedToPersonUuidRelationTypePersonAndContactData(
|
||||||
|
personUuid,
|
||||||
|
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()),
|
||||||
|
forLike(personData), forLike(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);
|
||||||
@ -77,7 +85,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(contactrealRepo.findByUuid(body.getContactUuid()).orElseThrow(
|
entityToSave.setContact(realContactRepo.findByUuid(body.getContactUuid()).orElseThrow(
|
||||||
() -> new NoSuchElementException("cannot find Contact by contactUuid: " + body.getContactUuid())
|
() -> new NoSuchElementException("cannot find Contact by contactUuid: " + body.getContactUuid())
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -144,6 +152,9 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String forLike(final String text) {
|
||||||
|
return text == null ? null : ("%" + text.toLowerCase() + "%");
|
||||||
|
}
|
||||||
|
|
||||||
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));
|
||||||
|
@ -29,6 +29,36 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
|
|||||||
""", nativeQuery = true)
|
""", nativeQuery = true)
|
||||||
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
|
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 lower-case string to match the persons tradeName, familyName or givenName (use '%' for wildcard)
|
||||||
|
* @param contactData a lower-case string to match the contacts caption, postalAddress, emailAddresses or phoneNumbers (use '%' for wildcard)
|
||||||
|
* @return a list of (accessible) relations which match all given criteria
|
||||||
|
*/
|
||||||
|
@Query(value = """
|
||||||
|
SELECT rel FROM HsOfficeRelationRbacEntity AS rel
|
||||||
|
WHERE (:relationType IS NULL OR CAST(rel.type AS String) = CAST(:relationType AS String))
|
||||||
|
AND ( :personUuid IS NULL OR
|
||||||
|
rel.anchor.uuid = :personUuid OR rel.holder.uuid = :personUuid )
|
||||||
|
AND ( :personData IS NULL OR
|
||||||
|
lower(rel.anchor.tradeName) LIKE :personData OR lower(rel.holder.tradeName) LIKE :personData OR
|
||||||
|
lower(rel.anchor.familyName) LIKE :personData OR lower(rel.holder.familyName) LIKE :personData 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.emailAddresses AS String)) LIKE :contactData OR
|
||||||
|
lower(CAST(rel.contact.phoneNumbers AS String)) LIKE :contactData )
|
||||||
|
""")
|
||||||
|
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidRelationTypePersonAndContactData(
|
||||||
|
UUID personUuid,
|
||||||
|
HsOfficeRelationType relationType,
|
||||||
|
String personData,
|
||||||
|
String contactData);
|
||||||
|
|
||||||
HsOfficeRelationRbacEntity save(final HsOfficeRelationRbacEntity entity);
|
HsOfficeRelationRbacEntity save(final HsOfficeRelationRbacEntity entity);
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
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: 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.
|
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.
|
||||||
|
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
|
||||||
@ -9,7 +11,7 @@ get:
|
|||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
- name: personUuid
|
- name: personUuid
|
||||||
in: query
|
in: query
|
||||||
required: true
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
@ -20,6 +22,18 @@ 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
|
||||||
|
@ -176,6 +176,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
|
|||||||
// given
|
// given
|
||||||
final var givenEntity = validEntityBuilder().build();
|
final var givenEntity = validEntityBuilder().build();
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
||||||
|
Dns.fakeResultForDomain(givenEntity.getIdentifier(), Dns.Result.fromRecords());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var errors = validator.validateContext(givenEntity);
|
final var errors = validator.validateContext(givenEntity);
|
||||||
@ -317,6 +318,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
|
|||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
||||||
|
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var errors = validator.validateContext(givenEntity);
|
final var errors = validator.validateContext(givenEntity);
|
||||||
@ -340,6 +342,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
|
|||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
||||||
|
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var zonefileErrors = new ArrayList<String>();
|
final var zonefileErrors = new ArrayList<String>();
|
||||||
|
@ -9,7 +9,6 @@ import net.hostsharing.hsadminng.context.Context;
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -55,7 +54,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
|
|||||||
class ListRelations {
|
class ListRelations {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType() throws JSONException {
|
void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
@ -113,7 +112,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void personAdmin_canViewAllRelationsOfGivenRelatedPersonAndAnyType() throws JSONException {
|
void personAdmin_canViewAllRelationsOfGivenRelatedPersonAndAnyType() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
context.define("contact-admin@firstcontact.example.com");
|
context.define("contact-admin@firstcontact.example.com");
|
||||||
@ -125,7 +124,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
|
|||||||
.port(port)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.get("http://localhost/api/hs/office/relations?personUuid=%s"
|
.get("http://localhost/api/hs/office/relations?personUuid=%s"
|
||||||
.formatted(givenPerson.getUuid(), HsOfficeRelationTypeResource.PARTNER))
|
.formatted(givenPerson.getUuid()))
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
@ -169,6 +168,50 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
|
|||||||
"""));
|
"""));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void globalAdmin_canViewAllRelationsWithGivenContactData() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
context.define("superuser-alex@hostsharing.net");
|
||||||
|
|
||||||
|
RestAssured // @formatter:off
|
||||||
|
.given()
|
||||||
|
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||||
|
.port(port)
|
||||||
|
.when()
|
||||||
|
.get("http://localhost/api/hs/office/relations?personData=firby&contactData=Contact-Admin@FirstContact.Example.COM")
|
||||||
|
.then().log().all().assertThat()
|
||||||
|
.statusCode(200)
|
||||||
|
.contentType("application/json")
|
||||||
|
.body("", lenientlyEquals("""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"anchor": {
|
||||||
|
"personType": "LEGAL_PERSON",
|
||||||
|
"tradeName": "First GmbH"
|
||||||
|
},
|
||||||
|
"holder": {
|
||||||
|
"personType": "NATURAL_PERSON",
|
||||||
|
"givenName": "Susan",
|
||||||
|
"familyName": "Firby"
|
||||||
|
},
|
||||||
|
"type": "REPRESENTATIVE",
|
||||||
|
"contact": {
|
||||||
|
"caption": "first contact",
|
||||||
|
"postalAddress": "Vorname Nachname\\nStraße Hnr\\nPLZ Stadt",
|
||||||
|
"emailAddresses": {
|
||||||
|
"main": "contact-admin@firstcontact.example.com"
|
||||||
|
},
|
||||||
|
"phoneNumbers": {
|
||||||
|
"phone_office": "+49 123 1234567"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
@ -33,3 +33,10 @@ logging:
|
|||||||
level:
|
level:
|
||||||
liquibase: WARN
|
liquibase: WARN
|
||||||
net.ttddyy.dsproxy.listener: DEBUG # HOWTO: log meaningful SQL statements
|
net.ttddyy.dsproxy.listener: DEBUG # HOWTO: log meaningful SQL statements
|
||||||
|
org.testcontainers: DEBUG
|
||||||
|
com.github.dockerjava: DEBUG
|
||||||
|
|
||||||
|
testcontainers:
|
||||||
|
network:
|
||||||
|
mode: host
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user