diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..d07dd9b0e2b0281fbf514a968b9451cb6af62f93 --- /dev/null +++ b/lombok.config @@ -0,0 +1,30 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +lombok.log.fieldName=LOG +lombok.log.slf4j.flagUsage = ERROR +lombok.log.log4j.flagUsage = ERROR +lombok.data.flagUsage = ERROR +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6772822cf565a491dc69b2ff6a29f000fae4dad9..45252f6f8c192a2c2193b81a2af6ee47bf9b8813 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,20 @@ <version>${mapstruct-processor.version}</version> </dependency> + <!-- commons --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + <!-- mongock --> <dependency> <groupId>io.mongock</groupId> diff --git a/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceDeserializer.java b/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..2a6a78f0d9dbfaba065908fa153d61f83d785933 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceDeserializer.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import de.ozgcloud.common.datatype.StringBasedValue; +import lombok.Getter; + +abstract class AbstractLinkedResourceDeserializer extends StdDeserializer<Object> implements ContextualDeserializer { + + private static final long serialVersionUID = 1L; + + @Getter + private BeanProperty beanProperty; + + @Getter + private final JavaType targetType; + + protected AbstractLinkedResourceDeserializer() { + super(Object.class); + targetType = null; + } + + protected AbstractLinkedResourceDeserializer(BeanProperty beanProperty) { + super(Object.class); + this.beanProperty = beanProperty; + this.targetType = beanProperty.getType(); + } + + @Override + public Object deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + if (jsonParser.isExpectedStartArrayToken()) { + Collection<Object> idList = targetType.getRawClass().isAssignableFrom(Set.class) ? new HashSet<>() : new ArrayList<>(); + + while (!jsonParser.nextToken().isStructEnd()) { + idList.add(extractId(jsonParser.getText())); + } + return idList; + } else { + return extractId(jsonParser.getText()); + } + } + + Object extractId(String url) { + Class<?> type; + if (targetType.isCollectionLikeType()) { + type = targetType.getContentType().getRawClass(); + } else { + type = targetType.getRawClass(); + } + + if (String.class.isAssignableFrom(type)) { + return extractStringId(url); + } + if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) { + return extractLongId(url); + } + if (StringBasedValue.class.isAssignableFrom(type)) { + return extractStringBasedValue(type, url); + } + return buildByBuilder(url); + } + + abstract Object buildByBuilder(String url); + + public static Long extractLongId(String uri) { + var trimedUri = StringUtils.trimToNull(uri); + if (Objects.isNull(trimedUri)) { + return null; + } + return Long.parseLong(URLDecoder.decode(trimedUri.substring(trimedUri.lastIndexOf('/') + 1), StandardCharsets.UTF_8)); + } + + private StringBasedValue extractStringBasedValue(Class<?> type, String url) { + String value = extractStringId(url); + Method fromMethod; + try { + fromMethod = type.getMethod("from", String.class); + } catch (NoSuchMethodException e) { + throw new IllegalStateException( + String.format("Cannot generate Id from type '%s'. Missing 'from' Method.", targetType.getRawClass().getSimpleName())); + } + try { + return (StringBasedValue) fromMethod.invoke(null, value); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new IllegalStateException( + String.format("Cannot generate Id from type '%s'. Error calling 'from' Method.", targetType.getRawClass().getSimpleName()), + e); + } + } + + public static String extractStringId(String url) { + return URLDecoder.decode(url.substring(url.lastIndexOf('/') + 1), StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceSerializer.java b/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..4e7ff8b53699d3c1f6d473f8992a20ddb548917b --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceSerializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +import de.ozgcloud.common.errorhandling.TechnicalException; + +abstract class AbstractLinkedResourceSerializer extends JsonSerializer<Object> implements ContextualSerializer { + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + + if (value instanceof Collection) { + gen.writeStartArray(); + ((Collection<?>) value).forEach(val -> writeObject(gen, buildLink(val))); + gen.writeEndArray(); + } else { + writeObject(gen, buildLink(value)); + } + } + + abstract String buildLink(Object id); + + abstract IdExtractor<Object> getExtractor(); + + void writeObject(JsonGenerator gen, Object value) { + try { + gen.writeObject(value); + } catch (IOException e) { + throw new TechnicalException("Error writing String to json", e); + } + } +} diff --git a/src/main/java/de/ozgcloud/admin/common/CollectionModelBuilder.java b/src/main/java/de/ozgcloud/admin/common/CollectionModelBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..d6976eac3335b4251b2b9e4bd4aeeb02f3b8112b --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/CollectionModelBuilder.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.Link; + +import lombok.RequiredArgsConstructor; + +public class CollectionModelBuilder<T> { + + private final Iterable<T> entities; + + private final List<Link> links = new LinkedList<>(); + + private CollectionModelBuilder(Iterable<T> entities) { + this.entities = entities; + } + + public static <T> CollectionModelBuilder<T> fromEntities(Iterable<T> entities) { + return new CollectionModelBuilder<>(entities); + } + + public static <T> CollectionModelBuilder<T> fromEntities(Stream<T> entities) { + return new CollectionModelBuilder<>(entities.toList()); + } + + public CollectionModelBuilder<T> addLink(Link link) { + links.add(link); + return this; + } + + public ConditionalLinkAdder ifMatch(BooleanSupplier guard) { + return new ConditionalLinkAdder(guard.getAsBoolean()); + } + + public ConditionalLinkAdder ifMatch(Predicate<? super Iterable<T>> predicate) { + return new ConditionalLinkAdder(predicate.test(entities)); + } + + public CollectionModel<T> buildModel() { + var builtModel = CollectionModel.of(entities); + builtModel.add(links); + return builtModel; + } + + @RequiredArgsConstructor + public class ConditionalLinkAdder { + + public final boolean conditionFulfilled; + + public CollectionModelBuilder<T> addLink(Link link) { + if (conditionFulfilled) { + links.add(link); + } + return CollectionModelBuilder.this; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/common/IdBuilder.java b/src/main/java/de/ozgcloud/admin/common/IdBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..17e590d1935ca727f001834823bfd26b4476b5fe --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/IdBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import com.fasterxml.jackson.databind.BeanProperty; + +import lombok.NoArgsConstructor; + +@NoArgsConstructor +class IdBuilder implements ObjectBuilder<Object> { + + @Override + public Object build(Object id) { + return id; + } + + @Override + public ObjectBuilder<Object> constructContextAware(BeanProperty property) { + return new IdBuilder(); + } +} diff --git a/src/main/java/de/ozgcloud/admin/common/IdExtractor.java b/src/main/java/de/ozgcloud/admin/common/IdExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..baed5fa621ee3e38bc11844b3e02abb7e4fbdf22 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/IdExtractor.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +@FunctionalInterface +public interface IdExtractor<T> { + String extractId(T object); +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/common/LinkedResource.java b/src/main/java/de/ozgcloud/admin/common/LinkedResource.java new file mode 100644 index 0000000000000000000000000000000000000000..f06b9786f97aab1f42ecb583c447edbdf16f50d7 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/LinkedResource.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.core.annotation.AliasFor; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Inherited + +@JacksonAnnotationsInside +@JsonSerialize(using = LinkedResourceSerializer.class) +@JsonDeserialize(using = LinkedResourceDeserializer.class) +public @interface LinkedResource { + + Class<?> controllerClass(); + + Class<? extends IdExtractor<Object>> extractor() default ToStringExtractor.class; + + @AliasFor(annotation = JsonDeserialize.class, attribute = "builder") + Class<? extends ObjectBuilder<Object>> builder() default IdBuilder.class; +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/common/LinkedResourceDeserializer.java b/src/main/java/de/ozgcloud/admin/common/LinkedResourceDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..6f312a3035ee1c85426cc1230e5ef8a58bb2dec1 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/LinkedResourceDeserializer.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.lang3.reflect.ConstructorUtils; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import de.ozgcloud.common.errorhandling.TechnicalException; + +public class LinkedResourceDeserializer extends AbstractLinkedResourceDeserializer { + + private static final long serialVersionUID = 1L; + + private LinkedResource annotation; + + protected LinkedResourceDeserializer() { + super(); + } + + protected LinkedResourceDeserializer(BeanProperty beanProperty) { + super(beanProperty); + this.annotation = beanProperty.getAnnotation(LinkedResource.class); + } + + @Override + Object buildByBuilder(String url) { + ObjectBuilder<?> builder; + try { + builder = ConstructorUtils.invokeConstructor(annotation.builder()).constructContextAware(getBeanProperty()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new TechnicalException("Error instanciating Builder.", e); + } + return builder.build(extractStringId(url)); + } + + @Override + public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) { + return new LinkedResourceDeserializer(property); + } + +} diff --git a/src/main/java/de/ozgcloud/admin/common/LinkedResourceSerializer.java b/src/main/java/de/ozgcloud/admin/common/LinkedResourceSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..698bfb2904de5f7bb4261c05c6fc4b74ba2d9ef8 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/LinkedResourceSerializer.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.lang3.reflect.ConstructorUtils; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class LinkedResourceSerializer extends AbstractLinkedResourceSerializer { + + private LinkedResource annotation; + + private LinkedResourceSerializer(LinkedResource annotation) { + this.annotation = annotation; + } + + @Override + public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) { + return new LinkedResourceSerializer(property.getAnnotation(LinkedResource.class)); + } + + @Override + String buildLink(Object id) { + return linkTo(annotation.controllerClass()).slash(getExtractor().extractId(id)).toString(); + } + + @Override + IdExtractor<Object> getExtractor() { + try { + return ConstructorUtils.invokeConstructor(annotation.extractor()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new TechnicalException("Error instanciating IdExtractor", e); + } + } +} diff --git a/src/main/java/de/ozgcloud/admin/common/ModelBuilder.java b/src/main/java/de/ozgcloud/admin/common/ModelBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..11f4c3c001fe908ed2f02b2100399ffb2670084a --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/ModelBuilder.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; + +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +public class ModelBuilder<T> { + + private static final Map<Class<?>, Map<Object, List<Field>>> ANNOTATED_FIELDS_BY_ANNOTATION = new ConcurrentHashMap<>(); + + private final T entity; + private final EntityModel<T> model; + + private final List<Link> links = new LinkedList<>(); + private final List<Function<EntityModel<T>, EntityModel<T>>> mapper = new LinkedList<>(); + + private ModelBuilder(T entity) { + this.entity = entity; + this.model = null; + } + + private ModelBuilder(EntityModel<T> model) { + this.entity = null; + this.model = model; + } + + public static <T> ModelBuilder<T> fromEntity(T entity) { + return new ModelBuilder<>(entity); + } + + public static <T> ModelBuilder<T> fromModel(EntityModel<T> model) { + return new ModelBuilder<>(model); + } + + public ModelBuilder<T> addLink(Link link) { + links.add(link); + return this; + } + + public ModelBuilder<T> addLink(Optional<Link> link) { + link.ifPresent(links::add); + return this; + } + + public ModelBuilder<T> addLinks(Link... links) { + this.links.addAll(Arrays.asList(links)); + return this; + } + + public ModelBuilder<T> addLinks(Collection<Link> links) { + this.links.addAll(links); + return this; + } + + public ConditionalLinkAdder ifMatch(Predicate<T> predicate) { + return new ConditionalLinkAdder(predicate.test(Objects.isNull(entity) ? model.getContent() : entity)); + } + + public ConditionalLinkAdder ifMatch(BooleanSupplier guard) { + return new ConditionalLinkAdder(guard.getAsBoolean()); + } + + public ModelBuilder<T> map(UnaryOperator<EntityModel<T>> mapper) { + this.mapper.add(mapper); + return this; + } + + public EntityModel<T> buildModel() { + var filteredLinks = links.stream().filter(Objects::nonNull).collect(Collectors.toSet()); + + EntityModel<T> buildedModel = Objects.isNull(model) ? EntityModel.of(entity) : model; + buildedModel = buildedModel.add(filteredLinks); + + addLinkByLinkedResourceAnnotationIfMissing(buildedModel); + + return applyMapper(buildedModel); + } + + private EntityModel<T> applyMapper(EntityModel<T> resource) { + Iterator<Function<EntityModel<T>, EntityModel<T>>> i = mapper.iterator(); + EntityModel<T> result = resource; + while (i.hasNext()) { + result = i.next().apply(result); + } + return result; + } + + private void addLinkByLinkedResourceAnnotationIfMissing(EntityModel<T> resource) { + getFields(LinkedResource.class).stream() + .filter(field -> shouldAddLink(resource, field)) + .forEach(field -> handleLinkedResourceField(resource, field)); + } + + private void handleLinkedResourceField(EntityModel<T> resource, Field field) { + getEntityFieldValue(field).map(Object::toString).filter(StringUtils::isNotBlank).ifPresent(val -> resource + .add(WebMvcLinkBuilder.linkTo(field.getAnnotation(LinkedResource.class).controllerClass()).slash(val) + .withRel(sanitizeName(field.getName())))); + } + + private boolean shouldAddLink(EntityModel<T> resource, Field field) { + return !(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType()) || resource.hasLink(sanitizeName(field.getName()))); + } + + private List<Field> getFields(Class<? extends Annotation> annotationClass) { + var fields = Optional.ofNullable(ANNOTATED_FIELDS_BY_ANNOTATION.get(getEntity().getClass())) + .map(fieldsByAnnotation -> fieldsByAnnotation.get(annotationClass)) + .orElseGet(Collections::emptyList); + + if (CollectionUtils.isEmpty(fields)) { + fields = FieldUtils.getFieldsListWithAnnotation(getEntity().getClass(), annotationClass); + + updateFields(annotationClass, fields); + } + return fields; + } + + private void updateFields(Class<? extends Annotation> annotationClass, List<Field> fields) { + var annotationMap = Optional.ofNullable(ANNOTATED_FIELDS_BY_ANNOTATION.get(getEntity().getClass())).orElseGet(HashMap::new); + annotationMap.put(annotationClass, fields); + + ANNOTATED_FIELDS_BY_ANNOTATION.put(annotationClass, annotationMap); + } + + private String sanitizeName(String fieldName) { + if (fieldName.endsWith("Id")) { + return fieldName.substring(0, fieldName.indexOf("Id")); + } + return fieldName; + } + + private Optional<Object> getEntityFieldValue(Field field) { + try { + field.setAccessible(true); + Optional<Object> value = Optional.ofNullable(field.get(getEntity())); + field.setAccessible(false); + return value; + } catch (IllegalArgumentException | IllegalAccessException e) { + LOG.warn("Cannot access field value of LinkedResource field.", e); + } + return Optional.empty(); + } + + private T getEntity() { + return Optional.ofNullable(entity == null ? model.getContent() : entity) + .orElseThrow(() -> new IllegalStateException("Entity must not null for ModelBuilding")); + } + + @RequiredArgsConstructor + public class ConditionalLinkAdder { + + public final boolean conditionFulfilled; + + public ModelBuilder<T> addLink(Supplier<Link> linkSupplier) { + if (conditionFulfilled) { + addLink(linkSupplier.get()); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLinkIfPresent(Supplier<Optional<Link>> linkSupplier) { + if (conditionFulfilled) { + addLink(linkSupplier.get()); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLink(Function<T, Link> linkBuilder) { + if (conditionFulfilled) { + addLink(linkBuilder.apply(getEntity())); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLink(Link link) { + if (conditionFulfilled) { + links.add(link); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLink(Optional<Link> link) { + if (conditionFulfilled) { + link.ifPresent(links::add); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLinks(Link... links) { + if (conditionFulfilled) { + ModelBuilder.this.links.addAll(Arrays.asList(links)); + } + return ModelBuilder.this; + } + + @SafeVarargs + public final ModelBuilder<T> addLinks(Supplier<Link>... linkSuppliers) { + if (conditionFulfilled) { + for (int i = 0; i < linkSuppliers.length; i++) { + ModelBuilder.this.links.add(linkSuppliers[i].get()); + } + } + return ModelBuilder.this; + } + + public final ModelBuilder<T> addLinks(Supplier<Collection<Link>> linksSupplier) { + if (conditionFulfilled) { + linksSupplier.get().forEach(ModelBuilder.this.links::add); + } + + return ModelBuilder.this; + } + + } +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/common/ObjectBuilder.java b/src/main/java/de/ozgcloud/admin/common/ObjectBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..6ea93551abebf8d4f17d6f57a56da9c5164555e6 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/ObjectBuilder.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import com.fasterxml.jackson.databind.BeanProperty; + +public interface ObjectBuilder<T> { + + T build(Object id); + + ObjectBuilder<T> constructContextAware(BeanProperty property); +} diff --git a/src/main/java/de/ozgcloud/admin/common/ToStringExtractor.java b/src/main/java/de/ozgcloud/admin/common/ToStringExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..4330fe6aa0e036b5dd128072d478c9232ed1141f --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/ToStringExtractor.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import lombok.NoArgsConstructor; + +@NoArgsConstructor +class ToStringExtractor implements IdExtractor<Object> { + + @Override + public String extractId(Object object) { + return object.toString(); + } +} diff --git a/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java b/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java index 03f5a50c605abcb5c1fe0acbe17c05861042a8e3..de0bca1a4ba026f84939faa1dae4f2b2dd90f6f9 100644 --- a/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java +++ b/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java @@ -44,7 +44,7 @@ abstract class GroupMapper { return null; } if (values.size() > 1 && values.stream().distinct().count() > 1) { - log.warn("Group contains multiple values for {}. The first one is taken.", keycloakApiProperties.getOrganisationsEinheitIdKey()); + LOG.warn("Group contains multiple values for {}. The first one is taken.", keycloakApiProperties.getOrganisationsEinheitIdKey()); } return values.getFirst(); } diff --git a/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java b/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java index 84e860cb2cbdee0312df576f903dd285e89e1bd9..c46364338fef285376784272d2f2275743a92f79 100644 --- a/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java +++ b/src/main/java/de/ozgcloud/admin/migration/MongockFailedEventListener.java @@ -34,6 +34,6 @@ public class MongockFailedEventListener implements ApplicationListener<SpringMig @Override public void onApplicationEvent(SpringMigrationFailureEvent event) { - log.error("Mongock migration failed", event.getMigrationResult()); + LOG.error("Mongock migration failed", event.getMigrationResult()); } } \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java b/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java index ad1558351ae353658dbe653bfe2630e627373489..e66855af555eb96ad054bf3ae9601d0c432a9fbb 100644 --- a/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java +++ b/src/main/java/de/ozgcloud/admin/migration/MongockStartEventListener.java @@ -34,6 +34,6 @@ public class MongockStartEventListener implements ApplicationListener<SpringMigr @Override public void onApplicationEvent(SpringMigrationStartedEvent event) { - log.info("Mongock start migration..."); + LOG.info("Mongock start migration..."); } } \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java b/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java index 95a7f76ee988dc8f1e72b8db6341da77514e5b54..176bc14fdfbbac69e00b16a2e649cf8b5db1050d 100644 --- a/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java +++ b/src/main/java/de/ozgcloud/admin/migration/MongockSuccessEventListener.java @@ -34,6 +34,6 @@ public class MongockSuccessEventListener implements ApplicationListener<SpringMi @Override public void onApplicationEvent(SpringMigrationSuccessEvent event) { - log.info("Mongock migration successfull", event.getMigrationResult()); + LOG.info("Mongock migration successfull", event.getMigrationResult()); } } \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheit.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheit.java index 55998463878e0a5a87a02dc84b9fec48b5c006c6..be5e37b3e0c48034d0b934455d41e4f7dd577640 100644 --- a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheit.java +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheit.java @@ -13,10 +13,12 @@ import lombok.extern.jackson.Jacksonized; @Getter @Builder(toBuilder = true) @Jacksonized -@Document(language = "german") +@Document(language = "german", collection = OrganisationsEinheit.COLLECTION_NAME) @TypeAlias("OrganisationsEinheit") public class OrganisationsEinheit { + static final String COLLECTION_NAME = "organisationsEinheit"; + @Id private String id; private String name; diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitController.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitController.java index 5197ad6c6a17efc068db1987a0a73893b2c23829..c1d8df5c9f5e370e50a3b29b6b5b746705a01fb8 100644 --- a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitController.java +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitController.java @@ -2,6 +2,8 @@ package de.ozgcloud.admin.organisationseinheit; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -10,11 +12,22 @@ import lombok.RequiredArgsConstructor; @RestController @RequestMapping(OrganisationsEinheitController.PATH) @RequiredArgsConstructor -class OrganisationsEinheitController { +public class OrganisationsEinheitController { static final String PATH = "/api/organisationseinheits"; // NOSONAR - public CollectionModel<EntityModel<OrganisationsEinheit>> getAll(){ + private final OrganisationsEinheitService organisationsEinheitService; + + private final OrganisationsEinheitModelAssembler assembler; + + @GetMapping + public CollectionModel<EntityModel<OrganisationsEinheit>> getAll() { + var organisationsEinheiten = organisationsEinheitService.getAllOrganisationsEinheiten(); + return assembler.toCollectionModel(organisationsEinheiten); + } + + @GetMapping("/{id}") + public EntityModel<OrganisationsEinheit> getById(@PathVariable String id) { throw new UnsupportedOperationException("Not implemented yet"); } } diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssembler.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..8378d8f7dba7b06fc3e8605cccdf9301fcb7cfa0 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssembler.java @@ -0,0 +1,32 @@ +package de.ozgcloud.admin.organisationseinheit; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.stream.StreamSupport; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.ozgcloud.admin.common.CollectionModelBuilder; +import de.ozgcloud.admin.common.ModelBuilder; + +@Component +class OrganisationsEinheitModelAssembler implements RepresentationModelAssembler<OrganisationsEinheit, EntityModel<OrganisationsEinheit>> { + + @Override + public EntityModel<OrganisationsEinheit> toModel(OrganisationsEinheit organisationsEinheit) { + return ModelBuilder.fromEntity(organisationsEinheit) + .addLink(linkTo(methodOn(OrganisationsEinheitController.class).getById(organisationsEinheit.getId())).withSelfRel()) + .buildModel(); + } + + @Override + public CollectionModel<EntityModel<OrganisationsEinheit>> toCollectionModel(Iterable<? extends OrganisationsEinheit> organisationsEinheiten) { + var models = StreamSupport.stream(organisationsEinheiten.spliterator(), false).map(this::toModel); + return CollectionModelBuilder.fromEntities(models) + .addLink(linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withSelfRel()) + .buildModel(); + } +} diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitService.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitService.java index e4f0dee20887cad830a95666be154a6c204a13c3..afed94e7e4691d89ec5e84faeec8bc4f22304219 100644 --- a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitService.java +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitService.java @@ -1,5 +1,8 @@ package de.ozgcloud.admin.organisationseinheit; +import java.util.List; + +import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; @@ -8,10 +11,18 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class OrganisationsEinheitService { - private final OrganisationsEinheitRepository organisationsEinheitRepository; + private final OrganisationsEinheitRepository repository; public OrganisationsEinheit saveOrganisationsEinheit(OrganisationsEinheit organisationsEinheit) { - return organisationsEinheitRepository.save(organisationsEinheit); + return repository.save(organisationsEinheit); + } + + public List<OrganisationsEinheit> getAllOrganisationsEinheiten() { + return repository.findAll(); + } + + public OrganisationsEinheit getOrganisationsEinheitById(String id) { + return repository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Organisationseinheit with id " + id + " not found")); } } diff --git a/src/test/java/de/ozgcloud/admin/common/CollectionModelBuilderTest.java b/src/test/java/de/ozgcloud/admin/common/CollectionModelBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bc4dd74e3f2b4121bf7fc681b8ab5de5cdc42fde --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/CollectionModelBuilderTest.java @@ -0,0 +1,92 @@ +package de.ozgcloud.admin.common; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.hateoas.Link; + +import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheit; +import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory; + +class CollectionModelBuilderTest { + + private final String HREF = "http://test"; + private final String REL = "rel"; + + @Nested + class TestBuildModel { + + @Test + void shouldBuildModel() { + var vorgang = OrganisationsEinheitTestFactory.create(); + + var model = CollectionModelBuilder.fromEntities(List.of(vorgang)).buildModel(); + + assertThat(model.getContent()).hasSize(1).first().usingRecursiveComparison().isEqualTo(vorgang); + } + } + + @Nested + class TestAddLink { + + @Test + void shouldAddLink() { + var model = CollectionModelBuilder.fromEntities(List.of()).addLink(Link.of(HREF, REL)).buildModel(); + + assertThat(model.getLinks()).hasSize(1).first().extracting(Link::getHref, link -> link.getRel().value()).containsExactly(HREF, REL); + } + } + + @Nested + class TestIfMatch { + + @Nested + class TestWithBooleanSupplier { + + @Test + void shouldAddLink() { + var model = CollectionModelBuilder.fromEntities(List.of()).ifMatch(() -> true).addLink(Link.of(HREF, REL)).buildModel(); + + assertThat(model.getLinks()).hasSize(1).first().extracting(Link::getHref, link -> link.getRel().value()).containsExactly(HREF, REL); + } + + @Test + void shouldNotAddLink() { + var model = CollectionModelBuilder.fromEntities(List.of()).ifMatch(() -> false).addLink(Link.of(HREF, REL)).buildModel(); + + assertThat(model.getLinks()).isEmpty(); + } + } + + @Nested + class TestWithPredicate { + + private final Stream<OrganisationsEinheit> wiedervorlageStream = Stream.of(OrganisationsEinheitTestFactory.create()); + + @Test + void shouldAddLink() { + var model = CollectionModelBuilder.fromEntities(wiedervorlageStream) + .ifMatch(wiedervorlagen -> true) + .addLink(Link.of(HREF, REL)) + .buildModel(); + + assertThat(model.getLinks()).hasSize(1).first().extracting(Link::getHref, link -> link.getRel().value()).containsExactly(HREF, REL); + } + + @Test + void shouldNotAddLink() { + var model = CollectionModelBuilder.fromEntities(wiedervorlageStream) + .ifMatch(wiedervorlagen -> false) + .addLink(Link.of(HREF, REL)) + .buildModel(); + + assertThat(model.getLinks()).isEmpty(); + } + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/common/EntityModelTestFactory.java b/src/test/java/de/ozgcloud/admin/common/EntityModelTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..99617d582151e26fa46c12e4ef40e585b18fa0eb --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/EntityModelTestFactory.java @@ -0,0 +1,20 @@ +package de.ozgcloud.admin.common; + +import org.springframework.hateoas.EntityModel; + +import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheit; +import lombok.NoArgsConstructor; + +public class EntityModelTestFactory { + + public static final NullableEntityModel NULLABLE = createNullable(); + + private static NullableEntityModel createNullable() { + return new NullableEntityModel(); + } + + @NoArgsConstructor + private static class NullableEntityModel extends EntityModel<OrganisationsEinheit> { + + } +} diff --git a/src/test/java/de/ozgcloud/admin/common/IdBuilderTest.java b/src/test/java/de/ozgcloud/admin/common/IdBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6a3d3a08d5b55434bc96d58632846d2da3936eab --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/IdBuilderTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.BeanProperty; + +class IdBuilderTest { + + @DisplayName("Test building ID when deserializing linked resources") + @Nested + class TestBuilingId { + private static final String ID = "id"; + + @Test + void shouldBuildId() { + IdBuilder idBuilder = new IdBuilder(); + + var idObject = idBuilder.build(ID); + + assertThat(idObject).isInstanceOf(Object.class).asString().isEqualTo(ID); + } + + @Test + void shouldCreateObjectBuilder() { + BeanProperty property = mock(BeanProperty.class); + ObjectBuilder<Object> idBuilder = new IdBuilder().constructContextAware(property); + + assertThat(idBuilder).isNotNull(); + } + } +} diff --git a/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java b/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java index 3cf919b51996d6d932b5fc51c0ba4079cbb9d563..7fec947e3c250ecaa53b158bfa266c05e57acf9e 100644 --- a/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java +++ b/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java @@ -26,20 +26,20 @@ public class KeycloakInitializer implements ApplicationContextInitializer<Config @SuppressWarnings("resource") private void initContainer() { if (keycloakContainer == null) { - log.info("Creating Keycloak-container..."); + LOG.info("Creating Keycloak-container..."); keycloakContainer = new KeycloakContainer().withRealmImportFile("keycloak/realm-export.json").withVerboseOutput(); } if (!keycloakContainer.isRunning()) { - log.info("Starting Keycloak-container..."); + LOG.info("Starting Keycloak-container..."); keycloakContainer.setWaitStrategy( Wait.forLogMessage(".*message\":\"started.*", 1).withStartupTimeout(Duration.ofMinutes(3))); keycloakContainer.start(); - log.info("Keycloak-container started"); + LOG.info("Keycloak-container started"); } } private void setProperties(ConfigurableApplicationContext applicationContext) { - log.info("Keycloak URL: {}", keycloakContainer.getAuthServerUrl()); + LOG.info("Keycloak URL: {}", keycloakContainer.getAuthServerUrl()); TestPropertyValues.of(AUTH_SERVER_URL + "=" + keycloakContainer.getAuthServerUrl()).applyTo(applicationContext); diff --git a/src/test/java/de/ozgcloud/admin/common/LinkedResourceDeserializerTest.java b/src/test/java/de/ozgcloud/admin/common/LinkedResourceDeserializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..85c2cffe20da1dfc5195087a66dc8ef069080f55 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/LinkedResourceDeserializerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import static org.assertj.core.api.Assertions.*; + +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.exc.StreamReadException; +import com.fasterxml.jackson.databind.DatabindException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.admin.common.user.TestId; +import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory; + +class LinkedResourceDeserializerTest { + private static final String TEST_JSON = "{\"id\":\"/api/vorgangs/" + OrganisationsEinheitTestFactory.ID + "\"}"; + + @DisplayName("Test the deserilization of linked resource json") + @Nested + class TestDeserialization { + @Test + void shouldDeserialize() throws IOException { + LinkedResourceTestObject res = new ObjectMapper().readValue(TEST_JSON.getBytes(), LinkedResourceTestObject.class); + + assertThat(res).hasFieldOrPropertyWithValue("id", TestId.from(OrganisationsEinheitTestFactory.ID)); + } + + } + +} diff --git a/src/test/java/de/ozgcloud/admin/common/LinkedResourceSerializerTest.java b/src/test/java/de/ozgcloud/admin/common/LinkedResourceSerializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dacf8bcedacb365629ea84c8e5366ed8f540b994 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/LinkedResourceSerializerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.admin.common.user.TestId; +import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory; + +class LinkedResourceSerializerTest { + + @DisplayName("Test the json serialization of linked resource annotations") + @Nested + class TestSerialization { + @Test + void shouldSerialize() throws JsonProcessingException { + var testObj = new LinkedResourceTestObject(TestId.from(OrganisationsEinheitTestFactory.ID)); + + String serialized = new ObjectMapper().writeValueAsString(testObj); + + assertThat(serialized).isEqualTo("{\"id\":\"/api/organisationseinheits/" + OrganisationsEinheitTestFactory.ID + "\"}"); + } + + } +} diff --git a/src/test/java/de/ozgcloud/admin/common/LinkedResourceTestObject.java b/src/test/java/de/ozgcloud/admin/common/LinkedResourceTestObject.java new file mode 100644 index 0000000000000000000000000000000000000000..f387e57475d5355f758eb1d2941d77fc51f4a74c --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/LinkedResourceTestObject.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import de.ozgcloud.admin.common.user.TestId; +import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitController; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +class LinkedResourceTestObject { + @LinkedResource(controllerClass = OrganisationsEinheitController.class) + private TestId id; +} diff --git a/src/test/java/de/ozgcloud/admin/common/ModelBuilderTest.java b/src/test/java/de/ozgcloud/admin/common/ModelBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..600e54392d19e7bf6b107ae6741f5b80488c926b --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/ModelBuilderTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.Mock; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.RequestMapping; + +import lombok.Builder; + +class ModelBuilderTest { + + @DisplayName("Add link by annotation if missing") + @Nested + class TestAddLinkByAnnotationIfMissing { + + private static final String USER_MANAGER_URL = "http://localhost"; + private static final String USER_MANAGER_PROFILE_TEMPLATE = "/api/profile/%s"; + + @Mock + private ApplicationContext context; + + private TestEntity entity = TestEntityTestFactory.create(); + + @Test + void shouldHaveAddLinkByLinkedResource() { + var model = ModelBuilder.fromEntity(entity).buildModel(); + + assertThat(model.getLink(TestController.FILE_REL).get().getHref()).isEqualTo(TestController.PATH + "/" + TestEntityTestFactory.FILE); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldNotAddLinkByLinkedRessourceIfFieldValueIsNotSet(String fileId) { + var model = ModelBuilder.fromEntity(TestEntityTestFactory.createBuilder().file(fileId).build()).buildModel(); + + assertThat(model.getLink(TestController.FILE_REL)).isEmpty(); + } + } +} + +@Builder +class TestEntity { + + @LinkedResource(controllerClass = TestController.class) + private String file; +} + +@RequestMapping(TestController.PATH) +class TestController { + + static final String PATH = "/api/test"; + + static final String USER_REL = "user"; + static final String FILE_REL = "file"; + +} + +class TestEntityTestFactory { + + static final String USER = UUID.randomUUID().toString(); + static final String FILE = UUID.randomUUID().toString(); + + public static TestEntity create() { + return createBuilder().build(); + } + + public static TestEntity.TestEntityBuilder createBuilder() { + return TestEntity.builder() + .file(FILE); + } +} diff --git a/src/test/java/de/ozgcloud/admin/common/user/TestId.java b/src/test/java/de/ozgcloud/admin/common/user/TestId.java new file mode 100644 index 0000000000000000000000000000000000000000..062ac78d9fed39e852260dec5f4ed938f3209346 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/user/TestId.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.common.user; + +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; + +import de.ozgcloud.common.datatype.StringBasedValue; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +@EqualsAndHashCode(callSuper = true) +public class TestId extends StringBasedValue { + + private static final long serialVersionUID = 1L; + + TestId(String userId) { + super(userId); + } + + public static TestId from(UUID userId) { + return TestId.from(userId.toString()); + } + + public static TestId from(String userId) { + return new TestId(userId); + } + + public static TestId empty() { + return new TestId(StringUtils.EMPTY); + } + +} diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0d21366aa7d9b424af8033d3ab7484f1c13a5ad2 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java @@ -0,0 +1,87 @@ +package de.ozgcloud.admin.organisationseinheit; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import lombok.SneakyThrows; + +class OrganisationsEinheitControllerTest { + + @InjectMocks + private OrganisationsEinheitController controller; + + @Mock + private OrganisationsEinheitService organisationsEinheitService; + + @Mock + private OrganisationsEinheitModelAssembler assembler; + + private MockMvc mockMvc; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetAll { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final List<OrganisationsEinheit> organisationsEinheiten = List.of(organisationsEinheit); + private final CollectionModel<EntityModel<OrganisationsEinheit>> collectionModel = CollectionModel.of( + List.of(EntityModel.of(organisationsEinheit))); + + @BeforeEach + void setUp() { + when(organisationsEinheitService.getAllOrganisationsEinheiten()).thenReturn(organisationsEinheiten); + when(assembler.toCollectionModel(organisationsEinheiten)).thenReturn(collectionModel); + } + + @Test + void shouldCallService() { + doRequest(); + + verify(organisationsEinheitService).getAllOrganisationsEinheiten(); + } + + @Test + void shouldCallAssembler() { + doRequest(); + + verify(assembler).toCollectionModel(organisationsEinheiten); + } + + @Test + void shouldReturnCollectionModel() { + var response = controller.getAll(); + + assertThat(response).isEqualTo(collectionModel); + } + + @SneakyThrows + @Test + void shouldReturnHttpOk() { + doRequest().andExpect(status().isOk()); + } + + @SneakyThrows + private ResultActions doRequest() { + return mockMvc.perform(get(OrganisationsEinheitController.PATH)); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d69013443509cd1ae2328fed6842d98eae379141 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java @@ -0,0 +1,84 @@ +package de.ozgcloud.admin.organisationseinheit; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; +import org.springframework.web.util.UriTemplate; + +class OrganisationsEinheitModelAssemblerTest { + + @Spy + @InjectMocks + private OrganisationsEinheitModelAssembler assembler; + + @Nested + class TestToModel { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + + @Test + void shouldAddSelfLink() { + var model = assembler.toModel(organisationsEinheit); + + assertThat(model.getLink(IanaLinkRelations.SELF)) + .isNotEmpty() + .get() + .extracting(Link::getHref) + .isEqualTo(new UriTemplate(OrganisationsEinheitController.PATH + "/{id}").expand(OrganisationsEinheitTestFactory.ID).toString()); + } + + @Test + void shouldReturnModel() { + var model = assembler.toModel(organisationsEinheit); + + assertThat(model.getContent()).isEqualTo(organisationsEinheit); + } + } + + @Nested + class TestToCollectionModel { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + + @BeforeEach + void setUp() { + doReturn(EntityModel.of(organisationsEinheit)).when(assembler).toModel(organisationsEinheit); + } + + @Test + void shouldAddSelfLink() { + var collectionModel = assembler.toCollectionModel(List.of(organisationsEinheit)); + + assertThat(collectionModel.getLink(IanaLinkRelations.SELF)) + .isNotEmpty() + .get() + .extracting(Link::getHref) + .isEqualTo(OrganisationsEinheitController.PATH); + } + + @Test + void shouldBuildModels() { + assembler.toCollectionModel(List.of(organisationsEinheit)); + + verify(assembler).toModel(organisationsEinheit); + } + + @Test + void shouldHaveModels() { + var collectionModel = assembler.toCollectionModel(List.of(organisationsEinheit)); + + assertThat(collectionModel.getContent()).extracting(EntityModel::getContent).containsExactly(organisationsEinheit); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java index 345b190daa67a011ac5ee4d417d88fada7374138..a545a28ac74d8fc8cc41b78d50542f3e691e16af 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java @@ -3,11 +3,15 @@ package de.ozgcloud.admin.organisationseinheit; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.List; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; class OrganisationsEinheitServiceTest { @@ -15,7 +19,7 @@ class OrganisationsEinheitServiceTest { private OrganisationsEinheitService service; @Mock - private OrganisationsEinheitRepository organisationsEinheitRepository; + private OrganisationsEinheitRepository repository; @Nested class TestSaveOrganisationsEinheit { @@ -24,14 +28,14 @@ class OrganisationsEinheitServiceTest { @BeforeEach void setUp() { - when(organisationsEinheitRepository.save(organisationsEinheit)).thenReturn(organisationsEinheit); + when(repository.save(organisationsEinheit)).thenReturn(organisationsEinheit); } @Test void shouldCallRepository() { service.saveOrganisationsEinheit(organisationsEinheit); - verify(organisationsEinheitRepository).save(organisationsEinheit); + verify(repository).save(organisationsEinheit); } @Test @@ -42,4 +46,63 @@ class OrganisationsEinheitServiceTest { } } + @Nested + class TestGetAllOrganisationsEinheiten { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final List<OrganisationsEinheit> organisationsEinheiten = List.of(organisationsEinheit); + + @BeforeEach + void setUp() { + when(repository.findAll()).thenReturn(organisationsEinheiten); + } + + @Test + void shouldCallRepository() { + service.getAllOrganisationsEinheiten(); + + verify(repository).findAll(); + } + + @Test + void shouldReturnAll() { + var gotOrganisationsEinheiten = service.getAllOrganisationsEinheiten(); + + assertThat(gotOrganisationsEinheiten).containsExactly(organisationsEinheit); + } + } + + @Nested + class TestGetOrganisationsEinheitById { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + + @Test + void shouldCallRepository() { + when(repository.findById(OrganisationsEinheitTestFactory.ID)).thenReturn(Optional.of(organisationsEinheit)); + + service.getOrganisationsEinheitById(OrganisationsEinheitTestFactory.ID); + + verify(repository).findById(OrganisationsEinheitTestFactory.ID); + } + + @Test + void shouldReturnOrganisationsEinheit() { + when(repository.findById(OrganisationsEinheitTestFactory.ID)).thenReturn(Optional.of(organisationsEinheit)); + + var gotOrganisationsEinheit = service.getOrganisationsEinheitById(OrganisationsEinheitTestFactory.ID); + + assertThat(gotOrganisationsEinheit).isEqualTo(organisationsEinheit); + } + + @Test + void shouldThrowResourceNotFoundException() { + when(repository.findById("not_exists")).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> service.getOrganisationsEinheitById("not_exists")) + .isInstanceOf(ResourceNotFoundException.class) + .hasMessage("Organisationseinheit with id not_exists not found"); + } + } + } \ No newline at end of file