refactoring for implicit creation of dependend hosting-assets #108
27
README.md
27
README.md
@ -550,12 +550,37 @@ Dependency versions can be automatically upgraded to the latest available versio
|
|||||||
gw useLatestVersions
|
gw useLatestVersions
|
||||||
```
|
```
|
||||||
|
|
||||||
Afterwards, `gw check` is automatically started.
|
Afterward, `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?
|
||||||
|
@ -9,15 +9,25 @@ import java.lang.annotation.Target;
|
|||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
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 = clazz.getAnnotation(DisplayAs.class);
|
final var displayNameAnnot = getDisplayNameAnnotation(clazz);
|
||||||
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) {
|
||||||
|
if (clazz == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var annot = clazz.getAnnotation(DisplayAs.class);
|
||||||
|
return annot != null ? annot : getDisplayNameAnnotation(clazz.getSuperclass());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
@ -5,17 +5,18 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsA
|
|||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource;
|
||||||
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.validators.BookingItemEntitySaveProcessor;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
||||||
import net.hostsharing.hsadminng.mapper.KeyValueMap;
|
import net.hostsharing.hsadminng.mapper.KeyValueMap;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
|
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
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;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
|
||||||
import jakarta.persistence.PersistenceContext;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -30,13 +31,13 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StrictMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsBookingItemRbacRepository bookingItemRepo;
|
private HsBookingItemRbacRepository bookingItemRepo;
|
||||||
|
|
||||||
@PersistenceContext
|
@Autowired
|
||||||
private EntityManager em;
|
private EntityManagerWrapper em;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@ -48,7 +49,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
|
|
||||||
final var entities = bookingItemRepo.findAllByProjectUuid(projectUuid);
|
final var entities = bookingItemRepo.findAllByProjectUuid(projectUuid);
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var resources = mapper.mapList(entities, HsBookingItemResource.class, RBAC_ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
return ResponseEntity.ok(resources);
|
return ResponseEntity.ok(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,15 +63,20 @@ 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 mapped = new BookingItemEntitySaveProcessor(em, entityToSave)
|
||||||
final var saved = HsBookingItemEntityValidatorRegistry.validated(em, bookingItemRepo.save(entityToSave));
|
.preprocessEntity()
|
||||||
|
.validateEntity()
|
||||||
|
.prepareForSave()
|
||||||
|
.save()
|
||||||
|
.validateContext()
|
||||||
|
.mapUsing(e -> mapper.map(e, HsBookingItemResource.class, ITEM_TO_RESOURCE_POSTMAPPER))
|
||||||
|
.revampProperties();
|
||||||
|
|
||||||
final var uri =
|
final var uri =
|
||||||
MvcUriComponentsBuilder.fromController(getClass())
|
MvcUriComponentsBuilder.fromController(getClass())
|
||||||
.path("/api/hs/booking/items/{id}")
|
.path("/api/hs/booking/items/{id}")
|
||||||
.buildAndExpand(saved.getUuid())
|
.buildAndExpand(mapped.getUuid())
|
||||||
.toUri();
|
.toUri();
|
||||||
final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
|
||||||
return ResponseEntity.created(uri).body(mapped);
|
return ResponseEntity.created(uri).body(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
result.ifPresent(entity -> em.detach(entity)); // prevent further LAZY-loading
|
result.ifPresent(entity -> em.detach(entity)); // prevent further LAZY-loading
|
||||||
return result
|
return result
|
||||||
.map(bookingItemEntity -> ResponseEntity.ok(
|
.map(bookingItemEntity -> ResponseEntity.ok(
|
||||||
mapper.map(bookingItemEntity, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER)))
|
mapper.map(bookingItemEntity, HsBookingItemResource.class, RBAC_ENTITY_TO_RESOURCE_POSTMAPPER)))
|
||||||
.orElseGet(() -> ResponseEntity.notFound().build());
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,18 +126,21 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
new HsBookingItemEntityPatcher(current).apply(body);
|
new HsBookingItemEntityPatcher(current).apply(body);
|
||||||
|
|
||||||
final var saved = bookingItemRepo.save(HsBookingItemEntityValidatorRegistry.validated(em, current));
|
final var saved = bookingItemRepo.save(HsBookingItemEntityValidatorRegistry.validated(em, current));
|
||||||
final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var mapped = mapper.map(saved, HsBookingItemResource.class, RBAC_ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsBookingItemRbacEntity, HsBookingItemResource> ENTITY_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()) {
|
||||||
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final BiConsumer<HsBookingItemRbacEntity, HsBookingItemResource> RBAC_ENTITY_TO_RESOURCE_POSTMAPPER = ITEM_TO_RESOURCE_POSTMAPPER::accept;
|
||||||
|
|
||||||
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.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()));
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,131 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemResource;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
// TODO.refa: introduce common base class with HsHostingAssetEntitySaveProcessor
|
||||||
|
/**
|
||||||
|
* Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsBookingItem into a readable API.
|
||||||
|
*/
|
||||||
|
public class BookingItemEntitySaveProcessor {
|
||||||
|
|
||||||
|
private final HsEntityValidator<HsBookingItem> validator;
|
||||||
|
private String expectedStep = "preprocessEntity";
|
||||||
|
private final EntityManager em;
|
||||||
|
private HsBookingItem entity;
|
||||||
|
private HsBookingItemResource resource;
|
||||||
|
|
||||||
|
public BookingItemEntitySaveProcessor(final EntityManager em, final HsBookingItem entity) {
|
||||||
|
this.em = em;
|
||||||
|
this.entity = entity;
|
||||||
|
this.validator = HsBookingItemEntityValidatorRegistry.forType(entity.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// initial step allowing to set default values before any validations
|
||||||
|
public BookingItemEntitySaveProcessor preprocessEntity() {
|
||||||
|
step("preprocessEntity", "validateEntity");
|
||||||
|
validator.preprocessEntity(entity);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// validates the entity itself including its properties
|
||||||
|
public BookingItemEntitySaveProcessor validateEntity() {
|
||||||
|
step("validateEntity", "prepareForSave");
|
||||||
|
MultiValidationException.throwIfNotEmpty(validator.validateEntity(entity));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
public BookingItemEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) {
|
||||||
|
step("validateEntity", "prepareForSave");
|
||||||
|
final var ignoreRegExpPatterns = Arrays.stream(ignoreRegExp).map(Pattern::compile).toList();
|
||||||
|
MultiValidationException.throwIfNotEmpty(
|
||||||
|
validator.validateEntity(entity).stream()
|
||||||
|
.filter(error -> ignoreRegExpPatterns.stream().noneMatch(p -> p.matcher(error).matches() ))
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// hashing passwords etc.
|
||||||
|
public BookingItemEntitySaveProcessor prepareForSave() {
|
||||||
|
step("prepareForSave", "save");
|
||||||
|
validator.prepareProperties(em, entity);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the entity using the given `saveFunction`.
|
||||||
|
*
|
||||||
|
* <p>`validator.postPersist(em, entity)` is NOT called.
|
||||||
|
* If any postprocessing is necessary, the saveFunction has to implement this.</p>
|
||||||
|
* @param saveFunction
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public BookingItemEntitySaveProcessor saveUsing(final Function<HsBookingItem, HsBookingItem> saveFunction) {
|
||||||
|
step("save", "validateContext");
|
||||||
|
entity = saveFunction.apply(entity);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the using the `EntityManager`, but does NOT ever merge the entity.
|
||||||
|
*
|
||||||
|
* <p>`validator.postPersist(em, entity)` is called afterwards with the entity guaranteed to be flushed to the database.</p>
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public BookingItemEntitySaveProcessor save() {
|
||||||
|
return saveUsing(e -> {
|
||||||
|
if (!em.contains(entity)) {
|
||||||
|
em.persist(entity);
|
||||||
|
}
|
||||||
|
em.flush(); // makes RbacEntity available as RealEntity if needed
|
||||||
|
validator.postPersist(em, entity);
|
||||||
|
return entity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits)
|
||||||
|
public BookingItemEntitySaveProcessor validateContext() {
|
||||||
|
step("validateContext", "mapUsing");
|
||||||
|
return HsEntityValidator.doWithEntityManager(em, () -> {
|
||||||
|
MultiValidationException.throwIfNotEmpty(validator.validateContext(entity));
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// maps entity to JSON resource representation
|
||||||
|
public BookingItemEntitySaveProcessor mapUsing(
|
||||||
|
final Function<HsBookingItem, HsBookingItemResource> mapFunction) {
|
||||||
|
step("mapUsing", "revampProperties");
|
||||||
|
resource = mapFunction.apply(entity);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// removes write-only-properties and ads computed-properties
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public HsBookingItemResource revampProperties() {
|
||||||
|
step("revampProperties", null);
|
||||||
|
final var revampedProps = validator.revampProperties(em, entity, (Map<String, Object>) resource.getResources());
|
||||||
|
resource.setResources(revampedProps);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes sure that the steps are called in the correct order.
|
||||||
|
// Could also be implemented using an interface per method, but that seems exaggerated.
|
||||||
|
private void step(final String current, final String next) {
|
||||||
|
if (!expectedStep.equals(current)) {
|
||||||
|
throw new IllegalStateException("expected " + expectedStep + " but got " + current);
|
||||||
|
}
|
||||||
|
expectedStep = next;
|
||||||
|
}
|
||||||
|
}
|
@ -48,10 +48,11 @@ public class HsBookingItemEntityValidatorRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> doValidate(final EntityManager em, final HsBookingItem bookingItem) {
|
public static List<String> doValidate(final EntityManager em, final HsBookingItem bookingItem) {
|
||||||
|
final var bookingItemValidator = HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType());
|
||||||
return HsEntityValidator.doWithEntityManager(em, () ->
|
return HsEntityValidator.doWithEntityManager(em, () ->
|
||||||
HsEntityValidator.sequentiallyValidate(
|
HsEntityValidator.sequentiallyValidate(
|
||||||
() -> HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateEntity(bookingItem),
|
() -> bookingItemValidator.validateEntity(bookingItem),
|
||||||
() -> HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateContext(bookingItem))
|
() -> bookingItemValidator.validateContext(bookingItem))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
||||||
@ -15,6 +16,9 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
|||||||
|
|
||||||
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 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 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() {
|
||||||
@ -24,6 +28,12 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
|||||||
.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)
|
||||||
|
@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjec
|
|||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectResource;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@ -25,7 +25,7 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsBookingProjectRbacRepository bookingProjectRepo;
|
private HsBookingProjectRbacRepository bookingProjectRepo;
|
||||||
|
@ -11,7 +11,7 @@ import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAsse
|
|||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
||||||
import net.hostsharing.hsadminng.mapper.KeyValueMap;
|
import net.hostsharing.hsadminng.mapper.KeyValueMap;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
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.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -35,7 +35,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsHostingAssetRbacRepository rbacAssetRepo;
|
private HsHostingAssetRbacRepository rbacAssetRepo;
|
||||||
|
@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.context.Context;
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountResource;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.iban4j.BicUtil;
|
import org.iban4j.BicUtil;
|
||||||
import org.iban4j.IbanUtil;
|
import org.iban4j.IbanUtil;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -24,7 +24,7 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeBankAccountRepository bankAccountRepo;
|
private HsOfficeBankAccountRepository bankAccountRepo;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.contact;
|
package net.hostsharing.hsadminng.hs.office.contact;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
|
||||||
@ -26,7 +26,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeContactRbacRepository contactRepo;
|
private HsOfficeContactRbacRepository contactRepo;
|
||||||
|
@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.context.Context;
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
|
||||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||||
@ -29,7 +29,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
|
private HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
|
||||||
|
@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopShar
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource;
|
||||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||||
@ -31,7 +31,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
||||||
|
@ -7,8 +7,8 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebito
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
|
||||||
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.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
|
import net.hostsharing.hsadminng.persistence.EntityExistsValidator;
|
||||||
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,7 +23,6 @@ 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
|
||||||
@ -34,7 +33,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeDebitorRepository debitorRepo;
|
private HsOfficeDebitorRepository debitorRepo;
|
||||||
@ -42,6 +41,9 @@ 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;
|
||||||
|
|
||||||
@ -84,10 +86,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
|||||||
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());
|
body.getDebitorRel().setType(DEBITOR.name());
|
||||||
final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
||||||
validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
||||||
validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
||||||
validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
entityValidator.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());
|
||||||
@ -160,15 +162,4 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembersh
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipResource;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@ -24,7 +24,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeMembershipRepository membershipRepo;
|
private HsOfficeMembershipRepository membershipRepo;
|
||||||
|
@ -2,18 +2,18 @@ package net.hostsharing.hsadminng.hs.office.membership;
|
|||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
||||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMembershipPatchResource> {
|
public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMembershipPatchResource> {
|
||||||
|
|
||||||
private final Mapper mapper;
|
private final StandardMapper mapper;
|
||||||
private final HsOfficeMembershipEntity entity;
|
private final HsOfficeMembershipEntity entity;
|
||||||
|
|
||||||
public HsOfficeMembershipEntityPatcher(
|
public HsOfficeMembershipEntityPatcher(
|
||||||
final Mapper mapper,
|
final StandardMapper mapper,
|
||||||
final HsOfficeMembershipEntity entity) {
|
final HsOfficeMembershipEntity entity) {
|
||||||
this.mapper = mapper;
|
this.mapper = mapper;
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
|
@ -12,7 +12,7 @@ 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.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.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.object.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;
|
||||||
@ -36,7 +36,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficePartnerRepository partnerRepo;
|
private HsOfficePartnerRepository partnerRepo;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.person;
|
package net.hostsharing.hsadminng.hs.office.person;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonInsertResource;
|
||||||
@ -23,7 +23,7 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficePersonRepository personRepo;
|
private HsOfficePersonRepository personRepo;
|
||||||
|
@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@ -28,7 +28,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeRelationRbacRepository relationRbacRepo;
|
private HsOfficeRelationRbacRepository relationRbacRepo;
|
||||||
|
@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeSepaMand
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateResource;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@ -28,7 +28,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeSepaMandateRepository sepaMandateRepo;
|
private HsOfficeSepaMandateRepository sepaMandateRepo;
|
||||||
|
@ -130,7 +130,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> revampProperties(final EntityManager em, final E entity, final Map<String, Object> config) {
|
public Map<String, Object> revampProperties(final EntityManager em, final E entity, final Map<String, Object> config) {
|
||||||
final var copy = new HashMap<>(config);
|
final var copy = config != null ? new HashMap<>(config) : new HashMap();
|
||||||
stream(propertyValidators).forEach(p -> {
|
stream(propertyValidators).forEach(p -> {
|
||||||
if (p.isWriteOnly()) {
|
if (p.isWriteOnly()) {
|
||||||
copy.remove(p.propertyName);
|
copy.remove(p.propertyName);
|
||||||
|
@ -11,18 +11,20 @@ import java.lang.reflect.Field;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A nicer API for ModelMapper.
|
* A nicer API for ModelMapper.
|
||||||
*/
|
*/
|
||||||
public class Mapper extends ModelMapper {
|
abstract class Mapper extends ModelMapper {
|
||||||
|
|
||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
EntityManager em;
|
EntityManager em;
|
||||||
|
|
||||||
public Mapper() {
|
Mapper() {
|
||||||
getConfiguration().setAmbiguityIgnored(true);
|
getConfiguration().setAmbiguityIgnored(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +47,12 @@ public class Mapper extends ModelMapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <D> D map(final Object source, final Class<D> destinationType) {
|
public <D> D map(final Object source, final Class<D> destinationType) {
|
||||||
|
return map("", source, destinationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <D> D map(final String namePrefix, final Object source, final Class<D> destinationType) {
|
||||||
final var target = super.map(source, destinationType);
|
final var target = super.map(source, destinationType);
|
||||||
for (Field f : destinationType.getDeclaredFields()) {
|
for (Field f : getDeclaredFieldsIncludingSuperClasses(destinationType)) {
|
||||||
if (f.getAnnotation(ManyToOne.class) == null) {
|
if (f.getAnnotation(ManyToOne.class) == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -64,18 +70,30 @@ public class Mapper extends ModelMapper {
|
|||||||
if (subEntityUuid == null) {
|
if (subEntityUuid == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ReflectionUtils.setField(f, target, findEntityById(f.getType(), subEntityUuid));
|
ReflectionUtils.setField(f, target, fetchEntity(namePrefix + f.getName() + ".uuid", f.getType(), subEntityUuid));
|
||||||
}
|
}
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object findEntityById(final Class<?> entityClass, final Object subEntityUuid) {
|
private static <D> Field[] getDeclaredFieldsIncludingSuperClasses(final Class<D> destinationType) {
|
||||||
// using getReference would be more efficent, but results in very technical error messages
|
if (destinationType == null) {
|
||||||
final var entity = em.find(entityClass, subEntityUuid);
|
return new Field[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stream.concat(
|
||||||
|
stream(destinationType.getDeclaredFields()),
|
||||||
|
stream(getDeclaredFieldsIncludingSuperClasses(destinationType.getSuperclass())))
|
||||||
|
.toArray(Field[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> E fetchEntity(final String propertyName, final Class<E> entityClass, final Object subEntityUuid) {
|
||||||
|
final var entity = em.getReference(entityClass, subEntityUuid);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
throw new ValidationException("Unable to find " + DisplayName.of(entityClass) + " by uuid: " + subEntityUuid);
|
throw new ValidationException(
|
||||||
|
"Unable to find " + DisplayName.of(entityClass) +
|
||||||
|
" by " + propertyName + ": " + subEntityUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <S, T> T map(final S source, final Class<T> targetClass, final BiConsumer<S, T> postMapper) {
|
public <S, T> T map(final S source, final Class<T> targetClass, final BiConsumer<S, T> postMapper) {
|
||||||
@ -86,4 +104,13 @@ public class Mapper extends ModelMapper {
|
|||||||
postMapper.accept(source, target);
|
postMapper.accept(source, target);
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <S, T> T map(final String namePrefix, final S source, final Class<T> targetClass, final BiConsumer<S, T> postMapper) {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var target = map(source, targetClass);
|
||||||
|
postMapper.accept(source, target);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.mapper;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class MapperConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Mapper modelMapper() {
|
|
||||||
return new Mapper();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,14 @@
|
|||||||
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A nicer API for ModelMapper in standard mode.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class StandardMapper extends Mapper {
|
||||||
|
|
||||||
|
public StandardMapper() {
|
||||||
|
getConfiguration().setAmbiguityIgnored(true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static org.modelmapper.convention.MatchingStrategies.STRICT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A nicer API for ModelMapper in strict mode.
|
||||||
|
*
|
||||||
|
* <p>This makes sure that resource.whateverUuid does not accidentally get mapped to entity.uuid,
|
||||||
|
* if resource.uuid does not exist.</p>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class StrictMapper extends Mapper {
|
||||||
|
|
||||||
|
public StrictMapper() {
|
||||||
|
getConfiguration().setMatchingStrategy(STRICT);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package net.hostsharing.hsadminng.persistence;
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.grant;
|
package net.hostsharing.hsadminng.rbac.grant;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacGrantResource;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacGrantResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -22,7 +22,7 @@ public class RbacGrantController implements RbacGrantsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RbacGrantRepository rbacGrantRepository;
|
private RbacGrantRepository rbacGrantRepository;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.role;
|
package net.hostsharing.hsadminng.rbac.role;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacRoleResource;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacRoleResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -18,7 +18,7 @@ public class RbacRoleController implements RbacRolesApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RbacRoleRepository rbacRoleRepository;
|
private RbacRoleRepository rbacRoleRepository;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.subject;
|
package net.hostsharing.hsadminng.rbac.subject;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource;
|
||||||
@ -21,7 +21,7 @@ public class RbacSubjectController implements RbacSubjectsApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RbacSubjectRepository rbacSubjectRepository;
|
private RbacSubjectRepository rbacSubjectRepository;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.test.cust;
|
package net.hostsharing.hsadminng.rbac.test.cust;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi;
|
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi;
|
||||||
import net.hostsharing.hsadminng.test.generated.api.v1.model.TestCustomerResource;
|
import net.hostsharing.hsadminng.test.generated.api.v1.model.TestCustomerResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -21,7 +21,7 @@ public class TestCustomerController implements TestCustomersApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TestCustomerRepository testCustomerRepository;
|
private TestCustomerRepository testCustomerRepository;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.test.pac;
|
package net.hostsharing.hsadminng.rbac.test.pac;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestPackagesApi;
|
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestPackagesApi;
|
||||||
@ -21,7 +21,7 @@ public class TestPackageController implements TestPackagesApi {
|
|||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Mapper mapper;
|
private StandardMapper mapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TestPackageRepository testPackageRepository;
|
private TestPackageRepository testPackageRepository;
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item;
|
package net.hostsharing.hsadminng.hs.booking.item;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository;
|
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
|
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
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.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
@ -28,13 +31,14 @@ import java.util.UUID;
|
|||||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||||
import static org.hamcrest.Matchers.matchesRegex;
|
import static org.hamcrest.Matchers.matchesRegex;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(HsBookingItemController.class)
|
@WebMvcTest(HsBookingItemController.class)
|
||||||
@Import(Mapper.class)
|
@Import({StrictMapper.class, JsonObjectMapperConfiguration.class})
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
class HsBookingItemControllerRestTest {
|
class HsBookingItemControllerRestTest {
|
||||||
|
|
||||||
@ -44,8 +48,12 @@ class HsBookingItemControllerRestTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
@Mock
|
@Autowired
|
||||||
EntityManager em;
|
@SuppressWarnings("unused") // not used in test, but in controller class
|
||||||
|
StrictMapper mapper;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
EntityManagerWrapper em;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
EntityManagerFactory emf;
|
EntityManagerFactory emf;
|
||||||
@ -56,6 +64,16 @@ class HsBookingItemControllerRestTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
HsBookingItemRbacRepository rbacBookingItemRepo;
|
HsBookingItemRbacRepository rbacBookingItemRepo;
|
||||||
|
|
||||||
|
@TestConfiguration
|
||||||
|
public static class TestConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EntityManager entityManager() {
|
||||||
|
return mock(EntityManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void init() {
|
void init() {
|
||||||
when(emf.createEntityManager()).thenReturn(em);
|
when(emf.createEntityManager()).thenReturn(em);
|
||||||
|
@ -35,7 +35,8 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
.project(project)
|
.project(project)
|
||||||
.caption("Test-Domain")
|
.caption("Test-Domain")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", "example.org")
|
entry("domainName", "example.org"),
|
||||||
|
entry("targetUnixUser", "xyz00")
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
.caption("Test-Domain")
|
.caption("Test-Domain")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", "example.org"),
|
entry("domainName", "example.org"),
|
||||||
|
entry("targetUnixUser", "xyz00"),
|
||||||
entry("verificationCode", "1234-5678-9100")
|
entry("verificationCode", "1234-5678-9100")
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
@ -73,7 +75,8 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
.project(project)
|
.project(project)
|
||||||
.caption("Test-Domain")
|
.caption("Test-Domain")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 253))
|
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 253)),
|
||||||
|
entry("targetUnixUser", "xyz00")
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -91,7 +94,8 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
.project(project)
|
.project(project)
|
||||||
.caption("Test-Domain")
|
.caption("Test-Domain")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 254))
|
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 254)),
|
||||||
|
entry("targetUnixUser", "xyz00")
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -102,6 +106,44 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
assertThat(result).contains("'D-12345:Test-Project:Test-Domain.resources.domainName' length is expected to be at max 253 but length of 'dfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.example.org' is 254");
|
assertThat(result).contains("'D-12345:Test-Project:Test-Domain.resources.domainName' length is expected to be at max 253 but length of 'dfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.asdfghijklmnopqrstuvwxyz0123456789.example.org' is 254");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acceptsValidUnixUser() {
|
||||||
|
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
||||||
|
.type(DOMAIN_SETUP)
|
||||||
|
.project(project)
|
||||||
|
.caption("Test-Domain")
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("domainName", "example.com"),
|
||||||
|
entry("targetUnixUser", "xyz00-test")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsInvalidUnixUser() {
|
||||||
|
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
||||||
|
.type(DOMAIN_SETUP)
|
||||||
|
.project(project)
|
||||||
|
.caption("Test-Domain")
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("domainName", "example.com"),
|
||||||
|
entry("targetUnixUser", "xyz00test")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).contains("'D-12345:Test-Project:Test-Domain.resources.targetUnixUser' = 'xyz00test' is not a valid unix-user name");
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(strings = {
|
@ValueSource(strings = {
|
||||||
"de", "com", "net", "org", "actually-any-top-level-domain",
|
"de", "com", "net", "org", "actually-any-top-level-domain",
|
||||||
@ -123,7 +165,8 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
.project(project)
|
.project(project)
|
||||||
.caption("Test-Domain")
|
.caption("Test-Domain")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", secondLevelRegistrarDomain)
|
entry("domainName", secondLevelRegistrarDomain),
|
||||||
|
entry("targetUnixUser", "xyz00")
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -148,7 +191,8 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
.project(project)
|
.project(project)
|
||||||
.caption("Test-Domain")
|
.caption("Test-Domain")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", secondLevelRegistrarDomain)
|
entry("domainName", secondLevelRegistrarDomain),
|
||||||
|
entry("targetUnixUser", "xyz00")
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -170,6 +214,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
// then
|
// then
|
||||||
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||||
"{type=string, propertyName=domainName, matchesRegEx=[^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}], matchesRegExDescription=is not a (non-top-level) fully qualified domain name, notMatchesRegEx=[[^.]+, (co|org|gov|ac|sch)\\.uk, (com|net|org|edu|gov|asn|id)\\.au, (co|ne|or|ac|go)\\.jp, (com|net|org|gov|edu|ac)\\.cn, (com|net|org|gov|edu|mil|art)\\.br, (co|net|org|gen|firm|ind)\\.in, (com|net|org|gob|edu)\\.mx, (gov|edu)\\.it, (co|net|org|govt|ac|school|geek|kiwi)\\.nz, (co|ne|or|go|re|pe)\\.kr], notMatchesRegExDescription=is a forbidden registrar-level domain name, maxLength=253, required=true, writeOnce=true}",
|
"{type=string, propertyName=domainName, matchesRegEx=[^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}], matchesRegExDescription=is not a (non-top-level) fully qualified domain name, notMatchesRegEx=[[^.]+, (co|org|gov|ac|sch)\\.uk, (com|net|org|edu|gov|asn|id)\\.au, (co|ne|or|ac|go)\\.jp, (com|net|org|gov|edu|ac)\\.cn, (com|net|org|gov|edu|mil|art)\\.br, (co|net|org|gen|firm|ind)\\.in, (com|net|org|gob|edu)\\.mx, (gov|edu)\\.it, (co|net|org|govt|ac|school|geek|kiwi)\\.nz, (co|ne|or|go|re|pe)\\.kr], notMatchesRegExDescription=is a forbidden registrar-level domain name, maxLength=253, required=true, writeOnce=true}",
|
||||||
|
"{type=string, propertyName=targetUnixUser, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}$|^[a-z][a-z0-9]{2}[0-9]{2}-[a-z0-9\\._-]+$], matchesRegExDescription=is not a valid unix-user name, maxLength=253, required=true, writeOnce=true}",
|
||||||
"{type=string, propertyName=verificationCode, minLength=12, maxLength=64, computed=IN_INIT}");
|
"{type=string, propertyName=verificationCode, minLength=12, maxLength=64, computed=IN_INIT}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
|
|||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
||||||
import net.hostsharing.hsadminng.mapper.Array;
|
import net.hostsharing.hsadminng.mapper.Array;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -52,7 +52,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(HsHostingAssetController.class)
|
@WebMvcTest(HsHostingAssetController.class)
|
||||||
@Import({Mapper.class, JsonObjectMapperConfiguration.class})
|
@Import({ StandardMapper.class, JsonObjectMapperConfiguration.class})
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
public class HsHostingAssetControllerRestTest {
|
public class HsHostingAssetControllerRestTest {
|
||||||
|
|
||||||
@ -63,10 +63,11 @@ public class HsHostingAssetControllerRestTest {
|
|||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
Mapper mapper;
|
@SuppressWarnings("unused") // not used in test, but in controller class
|
||||||
|
StandardMapper mapper;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
private EntityManagerWrapper em;
|
EntityManagerWrapper em;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
EntityManagerFactory emf;
|
EntityManagerFactory emf;
|
||||||
@ -90,6 +91,7 @@ public class HsHostingAssetControllerRestTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ListTestCases {
|
enum ListTestCases {
|
||||||
CLOUD_SERVER(
|
CLOUD_SERVER(
|
||||||
List.of(
|
List.of(
|
||||||
|
@ -42,7 +42,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
.project(project)
|
.project(project)
|
||||||
.type(HsBookingItemType.DOMAIN_SETUP)
|
.type(HsBookingItemType.DOMAIN_SETUP)
|
||||||
.resources(new HashMap<>(ofEntries(
|
.resources(new HashMap<>(ofEntries(
|
||||||
entry("domainName", domainName)
|
entry("domainName", domainName),
|
||||||
|
entry("targetUnixUser", "xyz00")
|
||||||
))));
|
))));
|
||||||
HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem);
|
HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem);
|
||||||
return HsHostingAssetRbacEntity.builder()
|
return HsHostingAssetRbacEntity.builder()
|
||||||
|
@ -1680,13 +1680,19 @@ public class ImportHostingAssets extends BaseOfficeDataImport {
|
|||||||
final var relatedProject = domainSetup.getSubHostingAssets().stream()
|
final var relatedProject = domainSetup.getSubHostingAssets().stream()
|
||||||
.map(ha -> ha.getAssignedToAsset() != null ? ha.getAssignedToAsset().getRelatedProject() : null)
|
.map(ha -> ha.getAssignedToAsset() != null ? ha.getAssignedToAsset().getRelatedProject() : null)
|
||||||
.findAny().orElseThrow();
|
.findAny().orElseThrow();
|
||||||
|
final var targetUnixUser = domainSetup.getSubHostingAssets().stream()
|
||||||
|
.filter(subAsset -> subAsset.getType() == DOMAIN_HTTP_SETUP)
|
||||||
|
.map(domainHttpSetup -> domainHttpSetup.getAssignedToAsset().getIdentifier())
|
||||||
|
.findAny().orElse(null);
|
||||||
final var bookingItem = HsBookingItemRealEntity.builder()
|
final var bookingItem = HsBookingItemRealEntity.builder()
|
||||||
.type(HsBookingItemType.DOMAIN_SETUP)
|
.type(HsBookingItemType.DOMAIN_SETUP)
|
||||||
.caption("BI " + domainSetup.getIdentifier())
|
.caption("BI " + domainSetup.getIdentifier())
|
||||||
.project((HsBookingProjectRealEntity) relatedProject)
|
.project((HsBookingProjectRealEntity) relatedProject)
|
||||||
//.validity(toPostgresDateRange(created, cancelled))
|
//.validity(toPostgresDateRange(created, cancelled))
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("domainName", domainSetup.getIdentifier())))
|
entry("domainName", domainSetup.getIdentifier()),
|
||||||
|
entry("targetUnixUser", targetUnixUser)
|
||||||
|
))
|
||||||
.build();
|
.build();
|
||||||
domainSetup.setBookingItem(bookingItem);
|
domainSetup.setBookingItem(bookingItem);
|
||||||
bookingItems.put(nextAvailableBookingItemId(), bookingItem);
|
bookingItems.put(nextAvailableBookingItemId(), bookingItem);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.bankaccount;
|
package net.hostsharing.hsadminng.hs.office.bankaccount;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.EnumSource;
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -25,7 +25,7 @@ class HsOfficeBankAccountControllerRestTest {
|
|||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
Mapper mapper;
|
StandardMapper mapper;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
HsOfficeBankAccountRepository bankAccountRepo;
|
HsOfficeBankAccountRepository bankAccountRepo;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.coopassets;
|
package net.hostsharing.hsadminng.hs.office.coopassets;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
|
import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.EnumSource;
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
@ -30,7 +30,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
Mapper mapper;
|
StandardMapper mapper;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
|
HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.coopshares;
|
package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
|
import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.EnumSource;
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
@ -30,7 +30,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
|
|||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
Mapper mapper;
|
StandardMapper mapper;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
||||||
|
@ -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 uuid: 00000000-0000-0000-0000-000000000000"));
|
.body("message", is("ERROR: [400] Unable to find RealRelation by debitorRelUuid: 00000000-0000-0000-0000-000000000000"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.membership;
|
|||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
|
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -32,7 +32,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(HsOfficeMembershipController.class)
|
@WebMvcTest(HsOfficeMembershipController.class)
|
||||||
@Import(Mapper.class)
|
@Import(StandardMapper.class)
|
||||||
public class HsOfficeMembershipControllerRestTest {
|
public class HsOfficeMembershipControllerRestTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -115,7 +115,7 @@ public class HsOfficeMembershipControllerRestTest {
|
|||||||
.andExpect(status().is4xxClientError())
|
.andExpect(status().is4xxClientError())
|
||||||
.andExpect(jsonPath("statusCode", is(400)))
|
.andExpect(jsonPath("statusCode", is(400)))
|
||||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||||
.andExpect(jsonPath("message", is("ERROR: [400] Unable to find Partner by uuid: " + givenPartnerUuid)));
|
.andExpect(jsonPath("message", is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -4,7 +4,7 @@ import io.hypersistence.utils.hibernate.type.range.Range;
|
|||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
|
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
@ -40,7 +40,7 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
@Mock
|
@Mock
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
private Mapper mapper = new Mapper();
|
private StandardMapper mapper = new StandardMapper();
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void initMocks() {
|
void initMocks() {
|
||||||
|
@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity;
|
|||||||
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.HsOfficeRelationRealRepository;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -36,7 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(HsOfficePartnerController.class)
|
@WebMvcTest(HsOfficePartnerController.class)
|
||||||
@Import(Mapper.class)
|
@Import(StandardMapper.class)
|
||||||
class HsOfficePartnerControllerRestTest {
|
class HsOfficePartnerControllerRestTest {
|
||||||
|
|
||||||
static final UUID GIVEN_MANDANTE_UUID = UUID.randomUUID();
|
static final UUID GIVEN_MANDANTE_UUID = UUID.randomUUID();
|
||||||
|
@ -195,7 +195,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
|
|||||||
.post("http://localhost/api/hs/office/sepamandates")
|
.post("http://localhost/api/hs/office/sepamandates")
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(400)
|
.statusCode(400)
|
||||||
.body("message", is("ERROR: [400] Unable to find BankAccount by uuid: 00000000-0000-0000-0000-000000000000"));
|
.body("message", is("ERROR: [400] Unable to find BankAccount with uuid 00000000-0000-0000-0000-000000000000"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +225,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
|
|||||||
.post("http://localhost/api/hs/office/sepamandates")
|
.post("http://localhost/api/hs/office/sepamandates")
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(400)
|
.statusCode(400)
|
||||||
.body("message", is("ERROR: [400] Unable to find Debitor by uuid: 00000000-0000-0000-0000-000000000000"));
|
.body("message", is("ERROR: [400] Unable to find Debitor with uuid 00000000-0000-0000-0000-000000000000"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.context;
|
package net.hostsharing.hsadminng.rbac.context;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import net.hostsharing.hsadminng.mapper.Array;
|
import net.hostsharing.hsadminng.mapper.Array;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -17,7 +17,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@DataJpaTest
|
@DataJpaTest
|
||||||
@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, Mapper.class })
|
@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, StandardMapper.class })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
class ContextIntegrationTests {
|
class ContextIntegrationTests {
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.role;
|
package net.hostsharing.hsadminng.rbac.role;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -30,7 +30,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(RbacRoleController.class)
|
@WebMvcTest(RbacRoleController.class)
|
||||||
@Import(Mapper.class)
|
@Import(StandardMapper.class)
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
class RbacRoleControllerRestTest {
|
class RbacRoleControllerRestTest {
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.subject;
|
package net.hostsharing.hsadminng.rbac.subject;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -31,7 +31,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(RbacSubjectController.class)
|
@WebMvcTest(RbacSubjectController.class)
|
||||||
@Import(Mapper.class)
|
@Import(StandardMapper.class)
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
class RbacSubjectControllerRestTest {
|
class RbacSubjectControllerRestTest {
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.rbac.test;
|
|||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
@ -27,7 +27,7 @@ class MapperUnitTest {
|
|||||||
EntityManager em;
|
EntityManager em;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
Mapper mapper;
|
StandardMapper mapper;
|
||||||
|
|
||||||
final UUID GIVEN_UUID = UUID.randomUUID();
|
final UUID GIVEN_UUID = UUID.randomUUID();
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ class MapperUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void mapsBeanWithExistingSubEntity() {
|
void mapsBeanWithExistingSubEntity() {
|
||||||
final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s1(new SubSourceBean1(GIVEN_UUID)).build();
|
final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s1(new SubSourceBean1(GIVEN_UUID)).build();
|
||||||
when(em.find(SubTargetBean1.class, GIVEN_UUID)).thenReturn(new SubTargetBean1(GIVEN_UUID, "xxx"));
|
when(em.getReference(SubTargetBean1.class, GIVEN_UUID)).thenReturn(new SubTargetBean1(GIVEN_UUID, "xxx"));
|
||||||
|
|
||||||
final var result = mapper.map(givenSource, TargetBean.class);
|
final var result = mapper.map(givenSource, TargetBean.class);
|
||||||
assertThat(result).usingRecursiveComparison().isEqualTo(
|
assertThat(result).usingRecursiveComparison().isEqualTo(
|
||||||
@ -81,27 +81,27 @@ class MapperUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void mapsBeanWithSubEntityNotFound() {
|
void mapsBeanWithSubEntityNotFound() {
|
||||||
final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s1(new SubSourceBean1(GIVEN_UUID)).build();
|
final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s1(new SubSourceBean1(GIVEN_UUID)).build();
|
||||||
when(em.find(SubTargetBean1.class, GIVEN_UUID)).thenReturn(null);
|
when(em.getReference(SubTargetBean1.class, GIVEN_UUID)).thenReturn(null);
|
||||||
|
|
||||||
final var exception = catchThrowable(() ->
|
final var exception = catchThrowable(() ->
|
||||||
mapper.map(givenSource, TargetBean.class)
|
mapper.map(givenSource, TargetBean.class)
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(exception).isInstanceOf(ValidationException.class)
|
assertThat(exception).isInstanceOf(ValidationException.class)
|
||||||
.hasMessage("Unable to find SubTargetBean1 by uuid: " + GIVEN_UUID);
|
.hasMessage("Unable to find SubTargetBean1 by s1.uuid: " + GIVEN_UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mapsBeanWithSubEntityNotFoundAndDisplayName() {
|
void mapsBeanWithSubEntityNotFoundAndDisplayName() {
|
||||||
final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s2(new SubSourceBean2(GIVEN_UUID)).build();
|
final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s2(new SubSourceBean2(GIVEN_UUID)).build();
|
||||||
when(em.find(SubTargetBean2.class, GIVEN_UUID)).thenReturn(null);
|
when(em.getReference(SubTargetBean2.class, GIVEN_UUID)).thenReturn(null);
|
||||||
|
|
||||||
final var exception = catchThrowable(() ->
|
final var exception = catchThrowable(() ->
|
||||||
mapper.map(givenSource, TargetBean.class)
|
mapper.map(givenSource, TargetBean.class)
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(exception).isInstanceOf(ValidationException.class)
|
assertThat(exception).isInstanceOf(ValidationException.class)
|
||||||
.hasMessage("Unable to find SomeDisplayName by uuid: " + GIVEN_UUID);
|
.hasMessage("Unable to find SomeDisplayName by s2.uuid: " + GIVEN_UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user