Compare commits
No commits in common. "dc4cd818b42c0511ddc4be4d264290bd4ee805fc" and "7c46fdb36d7de35640c8f694595e09ab34d21e48" have entirely different histories.
dc4cd818b4
...
7c46fdb36d
27
README.md
27
README.md
@ -550,37 +550,12 @@ Dependency versions can be automatically upgraded to the latest available versio
|
|||||||
gw useLatestVersions
|
gw useLatestVersions
|
||||||
```
|
```
|
||||||
|
|
||||||
Afterward, `gw check` is automatically started.
|
Afterwards, `gw check` is automatically started.
|
||||||
Please only commit+push to master if the check run shows no errors.
|
Please only commit+push to master if the check run shows no errors.
|
||||||
|
|
||||||
More infos, e.g. on blacklists see on the [project's website](https://github.com/patrikerdes/gradle-use-latest-versions-plugin).
|
More infos, e.g. on blacklists see on the [project's website](https://github.com/patrikerdes/gradle-use-latest-versions-plugin).
|
||||||
|
|
||||||
|
|
||||||
## Biggest Flaws in our Architecture
|
|
||||||
|
|
||||||
### The RBAC System is too Complicated
|
|
||||||
|
|
||||||
Now, where we have a better experience with what we really need from the RBAC system, we have learned
|
|
||||||
that and creates too many (grant- and role-) rows and too even tables which could be avoided completely.
|
|
||||||
|
|
||||||
The basic idea is always to always have a fixed set of ordered role-types which apply for all DB-tables under RBAC,
|
|
||||||
e.g. OWNER>ADMIN>AGENT\[>PROXY?\]>TENENT>REFERRER.
|
|
||||||
Grants between these for the same DB-row would be implicit by order comparision.
|
|
||||||
This way we would get rid of all explicit grants within the same DB-row
|
|
||||||
and would not need the `rbac.role` table anymore.
|
|
||||||
We would also reduce the depth of the expensive recursive CTE-query.
|
|
||||||
|
|
||||||
This has to be explored further.
|
|
||||||
For now, we just keep it in mind and
|
|
||||||
|
|
||||||
### The Mapper is Error-Prone
|
|
||||||
|
|
||||||
Where `org.modelmapper.ModelMapper` reduces bloat-code a lot and has some nice features about recursive data-structure mappings,
|
|
||||||
it often causes strange errors which are hard to fix.
|
|
||||||
E.g. the uuid of the target main object is often taken from an uuid of a sub-subject.
|
|
||||||
(For now, use `StrictMapper` to avoid this, for the case it happens.)
|
|
||||||
|
|
||||||
|
|
||||||
## How To ...
|
## How To ...
|
||||||
|
|
||||||
### How to Configure .pgpass for the Default PostgreSQL Database?
|
### How to Configure .pgpass for the Default PostgreSQL Database?
|
||||||
|
@ -11,18 +11,13 @@ import java.lang.annotation.Target;
|
|||||||
public @interface DisplayAs {
|
public @interface DisplayAs {
|
||||||
class DisplayName {
|
class DisplayName {
|
||||||
public static String of(final Class<?> clazz) {
|
public static String of(final Class<?> clazz) {
|
||||||
final var displayNameAnnot = getDisplayNameAnnotation(clazz);
|
final var displayNameAnnot = clazz.getAnnotation(DisplayAs.class);
|
||||||
return displayNameAnnot != null ? displayNameAnnot.value() : clazz.getSimpleName();
|
return displayNameAnnot != null ? displayNameAnnot.value() : clazz.getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String of(@NotNull final Object instance) {
|
public static String of(@NotNull final Object instance) {
|
||||||
return of(instance.getClass());
|
return of(instance.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DisplayAs getDisplayNameAnnotation(final Class<?> clazz) {
|
|
||||||
final var annot = clazz.getAnnotation(DisplayAs.class);
|
|
||||||
return annot != null ? annot : getDisplayNameAnnotation(clazz.getSuperclass());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebito
|
|||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
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.mapper.StandardMapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.persistence.EntityExistsValidator;
|
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -23,6 +23,7 @@ import jakarta.validation.ValidationException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
||||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -41,9 +42,6 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeRelationRealRepository relrealRepo;
|
private HsOfficeRelationRealRepository relrealRepo;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EntityExistsValidator entityValidator;
|
|
||||||
|
|
||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
@ -87,9 +85,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
if ( body.getDebitorRel() != null ) {
|
if ( body.getDebitorRel() != null ) {
|
||||||
body.getDebitorRel().setType(DEBITOR.name());
|
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);
|
||||||
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
||||||
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
||||||
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
||||||
entityToSave.setDebitorRel(relrealRepo.save(debitorRel));
|
entityToSave.setDebitorRel(relrealRepo.save(debitorRel));
|
||||||
} else {
|
} else {
|
||||||
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
||||||
@ -162,4 +160,15 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
|
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
|
||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO.impl: extract this to some generally usable class?
|
||||||
|
private <T extends BaseEntity<T>> T validateEntityExists(final String property, final T entitySkeleton) {
|
||||||
|
final var foundEntity = em.find(entitySkeleton.getClass(), entitySkeleton.getUuid());
|
||||||
|
if ( foundEntity == null) {
|
||||||
|
throw new ValidationException("Unable to find " + DisplayName.of(entitySkeleton) + " by " + property + ": " + entitySkeleton.getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) foundEntity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.persistence;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
|
||||||
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.validation.ValidationException;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class EntityExistsValidator {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EntityManagerWrapper em;
|
|
||||||
|
|
||||||
public <T extends BaseEntity<T>> void validateEntityExists(final String property, final T entitySkeleton) {
|
|
||||||
final var foundEntity = em.find(entityClass(entitySkeleton), entitySkeleton.getUuid());
|
|
||||||
if ( foundEntity == null) {
|
|
||||||
throw new ValidationException("Unable to find " + DisplayName.of(entitySkeleton) + " by " + property + ": " + entitySkeleton.getUuid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T extends BaseEntity<T>> Class<?> entityClass(final T entityOrProxy) {
|
|
||||||
final var entityClass = entityClass(entityOrProxy.getClass());
|
|
||||||
if (entityClass == null) {
|
|
||||||
throw new IllegalArgumentException("@Entity not found in superclass hierarchy of " + entityOrProxy.getClass());
|
|
||||||
}
|
|
||||||
return entityClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> entityClass(final Class<?> entityOrProxyClass) {
|
|
||||||
return entityOrProxyClass.isAnnotationPresent(Entity.class)
|
|
||||||
? entityOrProxyClass
|
|
||||||
: entityOrProxyClass.getSuperclass() == null
|
|
||||||
? null
|
|
||||||
: entityClass(entityOrProxyClass.getSuperclass());
|
|
||||||
}
|
|
||||||
}
|
|
@ -405,7 +405,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
|||||||
.post("http://localhost/api/hs/office/debitors")
|
.post("http://localhost/api/hs/office/debitors")
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(400)
|
.statusCode(400)
|
||||||
.body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contactUuid: 00000000-0000-0000-0000-000000000000"));
|
.body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contact.uuid: 00000000-0000-0000-0000-000000000000"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,7 +433,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
|||||||
.post("http://localhost/api/hs/office/debitors")
|
.post("http://localhost/api/hs/office/debitors")
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(400)
|
.statusCode(400)
|
||||||
.body("message", is("ERROR: [400] Unable to find RealRelation by debitorRelUuid: 00000000-0000-0000-0000-000000000000"));
|
.body("message", is("ERROR: [400] Unable to find RealRelation by debitorRel.uuid: 00000000-0000-0000-0000-000000000000"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user