fix Error code 500 in Relation.find without type (NullPointerException) (#109)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #109
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig 2024-09-27 11:00:58 +02:00
parent cb4aecb9c8
commit 4811c0328c
7 changed files with 165 additions and 8 deletions

View File

@ -21,6 +21,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
// TODO.refa: split HsOfficePersonEntity into Real+Rbac-Entity
@Entity
@Table(schema = "hs_office", name = "person_rv")
@Getter

View File

@ -52,7 +52,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
context.define(currentSubject, assumedRoles);
final var entities = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
mapper.map(relationType, HsOfficeRelationType.class));
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()));
final var resources = mapper.mapList(entities, HsOfficeRelationResource.class,
RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);

View File

@ -13,7 +13,7 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
Optional<HsOfficeRelationRbacEntity> findByUuid(UUID id);
default List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType.toString());
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType == null ? null : relationType.toString());
}
@Query(value = """

View File

@ -13,7 +13,7 @@ public interface HsOfficeRelationRealRepository extends Repository<HsOfficeRelat
Optional<HsOfficeRelationRealEntity> findByUuid(UUID id);
default List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType.toString());
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType == null ? null : relationType.toString());
}
@Query(value = """

View File

@ -0,0 +1,98 @@
package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.NATURAL_PERSON;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.REPRESENTATIVE;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Import( { Context.class, JpaAttempt.class })
class HsOfficeRealRelationRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired
HsOfficeRelationRealRepository relationRealRepo;
@Autowired
HsOfficePersonRepository personRepo;
@PersistenceContext
EntityManager em;
@MockBean
HttpServletRequest request;
@Nested
class FindRelations {
@Test
public void canFindAllRelationsOfGivenPerson() {
// given
final var personUuid = determinePersonUuid(NATURAL_PERSON, "Smith");
// when
final var result = relationRealRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid, null);
// then
context("superuser-alex@hostsharing.net"); // just to be able to access RBAc-entities persons+contact
exactlyTheseRelationsAreReturned(
result,
"rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')",
"rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Smith, Peter', contact='sixth contact')",
"rel(anchor='NP Smith, Peter', type='DEBITOR', holder='NP Smith, Peter', contact='third contact')",
"rel(anchor='IF Third OHG', type='SUBSCRIBER', mark='members-announce', holder='NP Smith, Peter', contact='third contact')"
);
}
@Test
public void canFindAllRelationsOfGivenPersonAndType() {
// given:
final var personUuid = determinePersonUuid(NATURAL_PERSON, "Smith");
// when:
final var result = relationRealRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid, REPRESENTATIVE);
// then:
context("superuser-alex@hostsharing.net"); // just to be able to access RBAc-entities persons+contact
exactlyTheseRelationsAreReturned(
result,
"rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')"
);
}
}
private UUID determinePersonUuid(final HsOfficePersonType type, final String familyName) {
return (UUID) em.createNativeQuery("""
SELECT uuid FROM hs_office.person p
WHERE p.personType = cast(:type as hs_office.PersonType) AND p.familyName = :familyName
""", UUID.class)
.setParameter("familyName", familyName)
.setParameter("type", type.toString())
.getSingleResult();
}
private void exactlyTheseRelationsAreReturned(
final List<HsOfficeRelationRealEntity> actualResult,
final String... relationNames) {
assertThat(actualResult)
.extracting(HsOfficeRelation::toString)
.containsExactlyInAnyOrder(relationNames);
}
}

View File

@ -55,7 +55,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
class ListRelations {
@Test
void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType_ifNoCriteriaGiven() throws JSONException {
void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType() throws JSONException {
// given
context.define("superuser-alex@hostsharing.net");
@ -111,6 +111,64 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
"""));
// @formatter:on
}
@Test
void personAdmin_canViewAllRelationsOfGivenRelatedPersonAndAnyType() throws JSONException {
// given
context.define("contact-admin@firstcontact.example.com");
final var givenPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0);
RestAssured // @formatter:off
.given()
.header("current-subject", "superuser-alex@hostsharing.net")
.port(port)
.when()
.get("http://localhost/api/hs/office/relations?personUuid=%s"
.formatted(givenPerson.getUuid(), HsOfficeRelationTypeResource.PARTNER))
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"anchor": {
"tradeName": "First GmbH"
},
"holder": {
"givenName": "Susan",
"familyName": "Firby"
},
"type": "REPRESENTATIVE",
"mark": null,
"contact": { "caption": "first contact" }
},
{
"anchor": {
"tradeName": "Hostsharing eG"
},
"holder": {
"tradeName": "First GmbH"
},
"type": "PARTNER",
"mark": null,
"contact": { "caption": "first contact" }
},
{
"anchor": {
"tradeName": "First GmbH"
},
"holder": {
"tradeName": "First GmbH"
},
"type": "DEBITOR",
"mark": null,
"contact": { "caption": "first contact" }
}
]
"""));
// @formatter:on
}
}
@Nested

View File

@ -163,7 +163,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
}
@Nested
class FindAllRelations {
class FindRelations {
@Test
public void globalAdmin_withoutAssumedRole_canViewAllRelationsOfArbitraryPerson() {
@ -193,7 +193,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
.findFirst().orElseThrow();
// when:
final var result = relationRbacRepo.findRelationRelatedToPersonUuid(person.getUuid());
final var result = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(person.getUuid(), null);
// then:
exactlyTheseRelationsAreReturned(