From 2a403a5775ea908c2ecf91b518e43d34e441dd5f Mon Sep 17 00:00:00 2001
From: Felix Reichenbach <felix.reichenbach@mgm-tp.com>
Date: Fri, 21 Feb 2025 17:05:42 +0100
Subject: [PATCH] OZG-3936 remove setting of UserProfileLink in ModelBuilder,
 avoid using ModelBuilder

---
 .../AktenzeichenModelProcessor.java           | 15 +++---
 .../alfa/bescheid/BescheidModelAssembler.java | 32 ++++++-------
 .../bescheid/BescheidVorgangProcessor.java    | 26 +++++------
 .../alfa/bescheid/DocumentModelAssembler.java |  6 +--
 .../alfa/common/LinkedResourceProcessor.java  | 20 ++++----
 .../de/ozgcloud/alfa/common/ModelBuilder.java | 15 ------
 .../alfa/export/ExportVorgangProcessor.java   | 16 +++----
 .../WiedervorlageModelAssembler.java          | 38 +++++++--------
 .../AktenzeichenModelProcessorTest.java       |  8 +---
 .../bescheid/BescheidModelAssemblerTest.java  | 14 ++++--
 .../BescheidVorgangProcessorTest.java         | 41 +++++++----------
 .../bescheid/DocumentModelAssemblerTest.java  |  8 +++-
 .../CollaborationVorgangProcessorTest.java    | 12 ++---
 .../common/LinkedResourceProcessorTest.java   |  2 +-
 .../alfa/common/ModelBuilderTest.java         | 46 -------------------
 .../export/ExportVorgangProcessorTest.java    | 10 ----
 .../historie/HistorieModelAssemblerTest.java  | 17 ++++---
 .../KommentarModelAssemblerTest.java          | 17 +++----
 ...LoeschAnforderungCommandProcessorTest.java |  7 ---
 .../LoeschAnforderungModelAssemblerTest.java  |  9 ----
 ...LoeschAnforderungVorgangProcessorTest.java |  4 --
 .../vorgang/VorgangModelAssemblerTest.java    | 16 -------
 .../VorgangWithEingangProcessorTest.java      | 24 ----------
 ...organgWithEingangCommandProcessorTest.java | 16 -------
 .../WiedervorlageModelAssemblerTest.java      | 17 +------
 25 files changed, 128 insertions(+), 308 deletions(-)

diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessor.java
index aa7ce0231e..7f76702862 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessor.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessor.java
@@ -33,7 +33,6 @@ import org.springframework.hateoas.LinkRelation;
 import org.springframework.hateoas.server.RepresentationModelProcessor;
 import org.springframework.stereotype.Component;
 
-import de.ozgcloud.alfa.common.ModelBuilder;
 import de.ozgcloud.alfa.common.command.CommandController;
 import de.ozgcloud.alfa.vorgang.Vorgang;
 import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
@@ -43,9 +42,11 @@ public class AktenzeichenModelProcessor implements RepresentationModelProcessor<
 
 	static final LinkRelation REL_SET_AKTENZEICHEN = LinkRelation.of("set_aktenzeichen");
 
-	static final List<Vorgang.VorgangStatus> SET_AKTENZEICHEN_STATUSES = List.of(Vorgang.VorgangStatus.ANGENOMMEN, Vorgang.VorgangStatus.IN_BEARBEITUNG);
+	static final List<Vorgang.VorgangStatus> SET_AKTENZEICHEN_STATUSES = List.of(Vorgang.VorgangStatus.ANGENOMMEN,
+			Vorgang.VorgangStatus.IN_BEARBEITUNG);
 
-	private static final Predicate<VorgangWithEingang> IS_SET_AKTENZEICHEN_ALLOWED = vorgang -> SET_AKTENZEICHEN_STATUSES.contains(vorgang.getStatus());
+	private static final Predicate<VorgangWithEingang> IS_SET_AKTENZEICHEN_ALLOWED = vorgang -> SET_AKTENZEICHEN_STATUSES
+			.contains(vorgang.getStatus());
 
 	@Override
 	public EntityModel<VorgangWithEingang> process(EntityModel<VorgangWithEingang> model) {
@@ -54,11 +55,9 @@ public class AktenzeichenModelProcessor implements RepresentationModelProcessor<
 		if (vorgang == null) {
 			return model;
 		}
+		model.addIf(IS_SET_AKTENZEICHEN_ALLOWED.test(vorgang), () -> linkTo(methodOn(CommandController.CommandByRelationController.class)
+				.createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(), null)).withRel(REL_SET_AKTENZEICHEN));
 
-		return ModelBuilder.fromModel(model)
-				.ifMatch(IS_SET_AKTENZEICHEN_ALLOWED)
-				.addLink(linkTo(methodOn(CommandController.CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(),
-						null)).withRel(REL_SET_AKTENZEICHEN))
-				.buildModel();
+		return model;
 	}
 }
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidModelAssembler.java
index f02c1ae859..723a74e134 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidModelAssembler.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidModelAssembler.java
@@ -26,6 +26,7 @@ package de.ozgcloud.alfa.bescheid;
 import static java.util.Optional.*;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
 
+import java.util.List;
 import java.util.Optional;
 import java.util.function.Predicate;
 
@@ -37,7 +38,6 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
 import org.springframework.stereotype.Component;
 
 import de.ozgcloud.alfa.bescheid.BescheidController.BescheidByVorgangController;
-import de.ozgcloud.alfa.common.ModelBuilder;
 import de.ozgcloud.alfa.common.binaryfile.BinaryFileController;
 import de.ozgcloud.alfa.common.command.CommandController;
 import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController;
@@ -88,22 +88,20 @@ public class BescheidModelAssembler implements RepresentationModelAssembler<Besc
 				methodOn(BescheidCommandController.class).createCommand(vorgangWithEingang.getId(), bescheid.getId(), bescheid.getVersion(),
 						null));
 
-		return ModelBuilder.fromEntity(bescheid)
-				.addLink(selfLink.withSelfRel())
-				.addLink(deleteLink.withRel(REL_DELETE))
-				.addLink(selfLink.withSelfRel())
-				.addLink(uploadBescheidFileLink.withRel(REL_UPLOAD_BESCHEID_FILE))
-				.addLink(uploadAttachmentLink.withRel(REL_UPLOAD_ATTACHMENT))
-				.ifMatch(HAS_ATTACHMENTS)
-				.addLink(attachmentsLink.withRel(REL_ATTACHMENTS))
-				.addLink(createCommandLink.withRel(REL_UPDATE))
-				.ifMatch(() -> canCreateBescheidDocumentAutomatically(vorgangWithEingang))
-				.addLink(createCommandLink.withRel(REL_CREATE_DOCUMENT))
-				.addLink(createCommandLink.withRel(REL_CREATE_DOCUMENT_FROM_FILE))
-				.ifMatch(() -> canSendMessageToAntragsteller(vorgangWithEingang))
-				.addLink(bescheidenUndSendenLink.withRel(REL_BESCHEIDEN_UND_SENDEN))
-				.addLink(createCommandLink.withRel(REL_BESCHEIDEN))
-				.buildModel();
+		return EntityModel.of(bescheid)
+				.add(List.of(selfLink.withSelfRel(),
+						deleteLink.withRel(REL_DELETE),
+						uploadBescheidFileLink.withRel(REL_UPLOAD_BESCHEID_FILE),
+						uploadAttachmentLink.withRel(REL_UPLOAD_ATTACHMENT),
+						createCommandLink.withRel(REL_BESCHEIDEN),
+						createCommandLink.withRel(REL_UPDATE),
+						createCommandLink.withRel(REL_CREATE_DOCUMENT_FROM_FILE)))
+				.addIf(HAS_ATTACHMENTS.test(bescheid),
+						() -> attachmentsLink.withRel(REL_ATTACHMENTS))
+				.addIf(canCreateBescheidDocumentAutomatically(vorgangWithEingang),
+						() -> createCommandLink.withRel(REL_CREATE_DOCUMENT))
+				.addIf(canSendMessageToAntragsteller(vorgangWithEingang),
+						() -> bescheidenUndSendenLink.withRel(REL_BESCHEIDEN_UND_SENDEN));
 	}
 
 	@Override
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java
index d9cab224fa..1af6b1ed3f 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java
@@ -26,7 +26,6 @@ package de.ozgcloud.alfa.bescheid;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
 
 import java.util.Objects;
-import java.util.function.BooleanSupplier;
 
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.LinkRelation;
@@ -35,7 +34,6 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
 import org.springframework.stereotype.Component;
 
 import de.ozgcloud.alfa.bescheid.BescheidController.BescheidByVorgangController;
-import de.ozgcloud.alfa.common.ModelBuilder;
 import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController;
 import de.ozgcloud.alfa.common.user.CurrentUserService;
 import de.ozgcloud.alfa.common.user.UserRole;
@@ -60,14 +58,14 @@ class BescheidVorgangProcessor implements RepresentationModelProcessor<EntityMod
 		if (Objects.isNull(vorgang) || currentUserService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)) {
 			return model;
 		}
-		return ModelBuilder.fromModel(model)
-				.ifMatch(isRetrievingDraftAllowed(vorgang))
-				.addLink(getDraftLink(vorgang.getId()).withRel(REL_DRAFT))
-				.ifMatch(isCreatingDraftAllowed(vorgang))
-				.addLink(getCreateBescheidDraftLink(vorgang.getId(), vorgang.getVersion()).withRel(REL_CREATE_DRAFT))
-				.ifMatch(() -> existsBescheid(vorgang.getId()))
-				.addLink(getBescheideLink(vorgang.getId()).withRel(REL_BESCHEIDE))
-				.buildModel();
+		model
+				.addIf(isRetrievingDraftAllowed(vorgang),
+						() -> getDraftLink(vorgang.getId()).withRel(REL_DRAFT))
+				.addIf(isCreatingDraftAllowed(vorgang),
+						() -> getCreateBescheidDraftLink(vorgang.getId(), vorgang.getVersion()).withRel(REL_CREATE_DRAFT))
+				.addIf(existsBescheid(vorgang.getId()),
+						() -> getBescheideLink(vorgang.getId()).withRel(REL_BESCHEIDE));
+		return model;
 	}
 
 	private WebMvcLinkBuilder getDraftLink(String vorgangId) {
@@ -86,12 +84,12 @@ class BescheidVorgangProcessor implements RepresentationModelProcessor<EntityMod
 		return linkTo(methodOn(BescheidByVorgangController.class).getAll(vorgangId));
 	}
 
-	BooleanSupplier isRetrievingDraftAllowed(Vorgang vorgang) {
-		return () -> isVorgangInBearbeitung(vorgang) && draftExists(vorgang);
+	boolean isRetrievingDraftAllowed(Vorgang vorgang) {
+		return isVorgangInBearbeitung(vorgang) && draftExists(vorgang);
 	}
 
-	BooleanSupplier isCreatingDraftAllowed(Vorgang vorgang) {
-		return () -> isVorgangInBearbeitung(vorgang) && !draftExists(vorgang);
+	boolean isCreatingDraftAllowed(Vorgang vorgang) {
+		return isVorgangInBearbeitung(vorgang) && !draftExists(vorgang);
 	}
 
 	boolean isVorgangInBearbeitung(Vorgang vorgang) {
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentModelAssembler.java
index cef3f165bc..5d01a2a663 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentModelAssembler.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentModelAssembler.java
@@ -29,7 +29,6 @@ import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.server.RepresentationModelAssembler;
 import org.springframework.stereotype.Component;
 
-import de.ozgcloud.alfa.common.ModelBuilder;
 import lombok.RequiredArgsConstructor;
 
 @Component
@@ -42,8 +41,7 @@ class DocumentModelAssembler implements RepresentationModelAssembler<Document, E
 	public EntityModel<Document> toModel(Document document) {
 		var selfLink = linkTo(methodOn(DocumentController.class).getDocument(document.getId()));
 
-		return ModelBuilder.fromEntity(document)
-				.addLink(selfLink.withSelfRel())
-				.buildModel();
+		return EntityModel.of(document)
+				.add(selfLink.withSelfRel());
 	}
 }
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/LinkedResourceProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/LinkedResourceProcessor.java
index e8bd48da57..ff6dc3cd92 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/LinkedResourceProcessor.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/LinkedResourceProcessor.java
@@ -22,37 +22,37 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 @Component
 @RequiredArgsConstructor
-public class LinkedResourceProcessor implements RepresentationModelProcessor<EntityModel<?>> {
+public class LinkedResourceProcessor<T> implements RepresentationModelProcessor<EntityModel<T>> {
 
 	private final UserManagerUrlProvider userManagerUrlProvider;
 
 	@Override
-	public EntityModel<?> process(EntityModel<?> model) {
+	public EntityModel<T> process(EntityModel<T> model) {
 		addLinkByLinkedResourceAnnotationIfMissing(model);
 		addLinkByLinkedUserProfileResourceAnnotationIfMissing(model);
 		return model;
 	}
 
-	void addLinkByLinkedResourceAnnotationIfMissing(EntityModel<?> model) {
+	void addLinkByLinkedResourceAnnotationIfMissing(EntityModel<T> model) {
 		getFields(LinkedResource.class, model.getContent())
 				.filter(field -> shouldAddLink(model, field))
 				.forEach(field -> addLinkForLinkedResourceField(model, field));
 	}
 
-	void addLinkForLinkedResourceField(EntityModel<?> model, Field field) {
+	void addLinkForLinkedResourceField(EntityModel<T> model, Field field) {
 		getEntityFieldValue(model.getContent(), field).map(Object::toString).filter(StringUtils::isNotBlank)
 				.ifPresent(val -> model
 						.add(WebMvcLinkBuilder.linkTo(field.getAnnotation(LinkedResource.class).controllerClass()).slash(val)
 								.withRel(trimIdSuffix(field.getName()))));
 	}
 
-	void addLinkByLinkedUserProfileResourceAnnotationIfMissing(EntityModel<?> resource) {
+	void addLinkByLinkedUserProfileResourceAnnotationIfMissing(EntityModel<T> resource) {
 		getFields(LinkedUserProfileResource.class, resource.getContent())
 				.filter(field -> shouldAddLink(resource, field))
 				.forEach(field -> addLinkForLinkedUserProfileResourceField(resource, field));
 	}
 
-	Stream<Field> getFields(Class<? extends Annotation> annotationClass, Object content) {
+	Stream<Field> getFields(Class<? extends Annotation> annotationClass, T content) {
 		if (Objects.isNull(content)) {
 			return Stream.empty();
 		}
@@ -60,21 +60,21 @@ public class LinkedResourceProcessor implements RepresentationModelProcessor<Ent
 				.filter(field -> field.isAnnotationPresent(annotationClass));
 	}
 
-	boolean shouldAddLink(EntityModel<?> resource, Field field) {
+	boolean shouldAddLink(EntityModel<T> resource, Field field) {
 		return !(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType()) || resource.hasLink(trimIdSuffix(field.getName())));
 	}
 
-	void addLinkForLinkedUserProfileResourceField(EntityModel<?> model, Field field) {
+	void addLinkForLinkedUserProfileResourceField(EntityModel<T> model, Field field) {
 		getEntityFieldValue(model.getContent(), field).map(Object::toString).filter(StringUtils::isNotBlank)
 				.ifPresent(value -> addUserProfileLink(model, field, value));
 	}
 
-	private void addUserProfileLink(EntityModel<?> model, Field field, String value) {
+	private void addUserProfileLink(EntityModel<T> model, Field field, String value) {
 		Optional.ofNullable(userManagerUrlProvider.getUserProfileTemplate()).filter(StringUtils::isNotBlank)
 				.ifPresent(template -> model.add(Link.of(template.formatted(value)).withRel(trimIdSuffix(field.getName()))));
 	}
 
-	private Optional<Object> getEntityFieldValue(Object content, Field field) {
+	private Optional<Object> getEntityFieldValue(T content, Field field) {
 		try {
 			field.setAccessible(true);
 			Optional<Object> value = Optional.ofNullable(field.get(content));
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/ModelBuilder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/ModelBuilder.java
index 79ec76f5f0..f4b0b7a57d 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/ModelBuilder.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/ModelBuilder.java
@@ -122,7 +122,6 @@ public class ModelBuilder<T> {
 		buildedModel = buildedModel.add(filteredLinks);
 
 		addLinkByLinkedResourceAnnotationIfMissing(buildedModel);
-		addLinkByLinkedUserProfileResourceAnnotationIfMissing(buildedModel);
 
 		return applyMapper(buildedModel);
 	}
@@ -148,20 +147,6 @@ public class ModelBuilder<T> {
 						.withRel(sanitizeName(field.getName()))));
 	}
 
-	private void addLinkByLinkedUserProfileResourceAnnotationIfMissing(EntityModel<T> resource) {
-		getFields(LinkedUserProfileResource.class).stream()
-				.filter(field -> shouldAddLink(resource, field))
-				.forEach(field -> handleLinkedUserProfileResourceField(resource, field));
-	}
-
-	private void handleLinkedUserProfileResourceField(EntityModel<T> resource, Field field) {
-		getEntityFieldValue(field).ifPresent(val -> {
-			if (UserProfileUrlProvider.isConfigured()) {
-				resource.add(Link.of(UserProfileUrlProvider.getUrl(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())));
 	}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java
index 33e5c38b35..2cd81ac753 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java
@@ -33,7 +33,6 @@ import org.springframework.hateoas.LinkRelation;
 import org.springframework.hateoas.server.RepresentationModelProcessor;
 import org.springframework.stereotype.Component;
 
-import de.ozgcloud.alfa.common.ModelBuilder;
 import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController;
 import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus;
 import de.ozgcloud.alfa.vorgang.VorgangController;
@@ -59,14 +58,13 @@ class ExportVorgangProcessor implements RepresentationModelProcessor<EntityModel
 		if (Objects.isNull(vorgang)) {
 			return model;
 		}
-
-		return ModelBuilder.fromModel(model)
-				.ifMatch(IS_VORGANG_ABGESCHLOSSEN.and(vorgangController::isEditable))
-				.addLink(linkTo(methodOn(ExportVorgangController.class).exportVorgang(vorgang.getId())).withRel(REL_EXPORT))
-				.ifMatch(IS_VORGANG_ABGESCHLOSSEN.and(isDmsEnabled()).and(vorgangController::isEditable))
-				.addLink(linkTo(methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(),
-						null)).withRel(REL_ARCHIVE))
-				.buildModel();
+		model
+				.addIf(IS_VORGANG_ABGESCHLOSSEN.and(vorgangController::isEditable).test(vorgang),
+						() -> linkTo(methodOn(ExportVorgangController.class).exportVorgang(vorgang.getId())).withRel(REL_EXPORT))
+				.addIf(IS_VORGANG_ABGESCHLOSSEN.and(isDmsEnabled()).and(vorgangController::isEditable).test(vorgang),
+						() -> linkTo(methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(),
+								null)).withRel(REL_ARCHIVE));
+		return model;
 	}
 
 	private Predicate<VorgangWithEingang> isDmsEnabled() {
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssembler.java
index 2a94ca0fcd..a058e87ad7 100644
--- a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssembler.java
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssembler.java
@@ -28,21 +28,20 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.hateoas.CollectionModel;
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.server.RepresentationModelAssembler;
 import org.springframework.stereotype.Component;
 
-import de.ozgcloud.alfa.common.CollectionModelBuilder;
-import de.ozgcloud.alfa.common.ModelBuilder;
 import de.ozgcloud.alfa.common.binaryfile.BinaryFileController;
 import de.ozgcloud.alfa.vorgang.VorgangController;
 import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
 import de.ozgcloud.alfa.wiedervorlage.WiedervorlageCommandController.WiedervorlageCommandByVorgangController;
 import de.ozgcloud.alfa.wiedervorlage.WiedervorlageController.AttachmentsByWiedervorlageController;
+import lombok.RequiredArgsConstructor;
 
 @Component
+@RequiredArgsConstructor
 class WiedervorlageModelAssembler implements RepresentationModelAssembler<Wiedervorlage, EntityModel<Wiedervorlage>> {
 
 	static final String REL_EDIT = "edit";
@@ -59,8 +58,7 @@ class WiedervorlageModelAssembler implements RepresentationModelAssembler<Wieder
 	public static final String WIEDERVORLAGE_ATTACHMENT_PATH = "wiedervorlageAttachment";
 	public static final String FILE_PATH = "file";
 
-	@Autowired
-	private VorgangController vorgangController;
+	private final VorgangController vorgangController;
 
 	@Override
 	public EntityModel<Wiedervorlage> toModel(Wiedervorlage wiedervorlage) {
@@ -73,26 +71,24 @@ class WiedervorlageModelAssembler implements RepresentationModelAssembler<Wieder
 		var commandLink = linkTo(
 				methodOn(WiedervorlageCommandController.class).updateWiedervorlage(null, wiedervorlage.getId(), wiedervorlage.getVersion()));
 
-		return ModelBuilder.fromEntity(wiedervorlage).addLink(selfLink.withSelfRel())
-				.ifMatch(() -> vorgangController.isEditable(vorgang))
-				.addLink(commandLink.withRel(REL_EDIT))
-				.ifMatch(ALLOW_ERLEDIGEN).addLink(commandLink.withRel(REL_ERLEDIGEN))
-				.ifMatch(ALLOW_WIEDEREROEFFNEN).addLink(commandLink.withRel(REL_WIEDEREROEFFNEN))
-				.ifMatch(HAS_ATTACHMENTS)
-				.addLink(linkTo(methodOn(AttachmentsByWiedervorlageController.class).getAttachments(wiedervorlage.getId())).withRel(REL_ATTACHMENTS))
-				.buildModel();
+		return EntityModel.of(wiedervorlage)
+				.add(selfLink.withSelfRel())
+				.addIf(vorgangController.isEditable(vorgang), () -> commandLink.withRel(REL_EDIT))
+				.addIf(ALLOW_ERLEDIGEN.test(wiedervorlage), () -> commandLink.withRel(REL_ERLEDIGEN))
+				.addIf(ALLOW_WIEDEREROEFFNEN.test(wiedervorlage), () -> commandLink.withRel(REL_WIEDEREROEFFNEN))
+				.addIf(HAS_ATTACHMENTS.test(wiedervorlage),
+						() -> linkTo(methodOn(AttachmentsByWiedervorlageController.class).getAttachments(wiedervorlage.getId()))
+								.withRel(REL_ATTACHMENTS));
 	}
 
 	public CollectionModel<EntityModel<Wiedervorlage>> toCollectionModel(Stream<Wiedervorlage> entities, String vorgangId) {
 		var vorgang = vorgangController.getVorgang(vorgangId);
-
-		return CollectionModelBuilder.fromEntities(entities.map(wiedervorlage -> buildModel(wiedervorlage, vorgang)))
-				.addLink(linkTo(WiedervorlageController.class).withSelfRel())
-				.ifMatch(wiedervorlagenModel -> vorgangController.isEditable(vorgang))
-				.addLink(linkTo(methodOn(WiedervorlageCommandByVorgangController.class).createWiedervorlage(null, vorgangId))
-						.withRel(REL_CREATE))
-				.addLink(linkTo(BinaryFileController.class).slash(vorgangId).slash(WIEDERVORLAGE_ATTACHMENT_PATH).slash(FILE_PATH)
+		return CollectionModel.of(entities.map(wiedervorlage -> buildModel(wiedervorlage, vorgang)).toList())
+				.add(linkTo(WiedervorlageController.class).withSelfRel())
+				.add(linkTo(BinaryFileController.class).slash(vorgangId).slash(WIEDERVORLAGE_ATTACHMENT_PATH).slash(FILE_PATH)
 						.withRel(REL_UPLOAD_FILE))
-				.buildModel();
+				.addIf(vorgangController.isEditable(vorgang),
+						() -> linkTo(methodOn(WiedervorlageCommandByVorgangController.class).createWiedervorlage(null, vorgangId))
+								.withRel(REL_CREATE));
 	}
 }
\ No newline at end of file
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessorTest.java
index 98c3b71d19..6c0447d5e0 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/aktenzeichen/AktenzeichenModelProcessorTest.java
@@ -23,7 +23,6 @@
  */
 package de.ozgcloud.alfa.aktenzeichen;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
@@ -65,8 +64,6 @@ class AktenzeichenModelProcessorTest {
 		@ParameterizedTest
 		@EnumSource(names = { "ANGENOMMEN", "IN_BEARBEITUNG" }, mode = EnumSource.Mode.EXCLUDE)
 		void shouldNotCreateSetAktenzeichenLink(Vorgang.VorgangStatus vorgangStatus) {
-			initUserProfileUrlProvider(urlProvider);
-
 			var model = processor.process(buildModelWithVorgangStatus(vorgangStatus));
 
 			assertThat(model.getLink(AktenzeichenModelProcessor.REL_SET_AKTENZEICHEN)).isEmpty();
@@ -75,15 +72,12 @@ class AktenzeichenModelProcessorTest {
 		@ParameterizedTest
 		@EnumSource(names = { "ANGENOMMEN", "IN_BEARBEITUNG" })
 		void shouldCreateSetAktenzeichenLink(Vorgang.VorgangStatus vorgangStatus) {
-			initUserProfileUrlProvider(urlProvider);
-
 			var model = processor.process(buildModelWithVorgangStatus(vorgangStatus));
 
 			assertThat(model.getLink(AktenzeichenModelProcessor.REL_SET_AKTENZEICHEN)).isPresent().get()
 					.extracting(Link::getHref)
 					.isEqualTo(UriTemplate.of(CommandController.CommandByRelationController.COMMAND_BY_RELATION_PATH)
-							.expand(VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION).toString()
-					);
+							.expand(VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION).toString());
 		}
 	}
 
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidModelAssemblerTest.java
index 8c207f58d0..65a005968b 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidModelAssemblerTest.java
@@ -46,7 +46,7 @@ import org.springframework.hateoas.Link;
 import org.springframework.web.util.UriTemplate;
 
 import de.ozgcloud.alfa.bescheid.BescheidController.BescheidByVorgangController;
-import de.ozgcloud.alfa.common.ModelBuilder;
+import de.ozgcloud.alfa.common.LinkedResourceProcessor;
 import de.ozgcloud.alfa.common.binaryfile.FileId;
 import de.ozgcloud.alfa.common.command.CommandController;
 import de.ozgcloud.alfa.postfach.PostfachMailController;
@@ -67,6 +67,9 @@ class BescheidModelAssemblerTest {
 	@InjectMocks
 	private BescheidModelAssembler assembler;
 
+	@InjectMocks
+	private LinkedResourceProcessor linkedResourceProcessor;
+
 	@Mock
 	private BescheidService bescheidService;
 
@@ -213,7 +216,9 @@ class BescheidModelAssemblerTest {
 
 		@Test
 		void shouldNotHaveBescheidenUndSendenLinkOnVorgangWithoutServiceKonto() {
-			var vorgang = vorgangWithEingang.builder().header(VorgangHeadTestFactory.createBuilder().serviceKonto(null).build()).build();
+			var vorgang = VorgangWithEingangTestFactory.createBuilder()
+					.header(VorgangHeadTestFactory.createBuilder().serviceKonto(null).build())
+					.build();
 			when(vorgangController.getVorgang(VorgangHeaderTestFactory.ID)).thenReturn(vorgang);
 			when(postfachMailController.isPostfachConfigured()).thenReturn(true);
 
@@ -256,8 +261,9 @@ class BescheidModelAssemblerTest {
 			return callToModel(bescheid);
 		}
 
+		@SuppressWarnings("unchecked")
 		private EntityModel<Bescheid> callToModel(Bescheid bescheid) {
-			return assembler.toModel(bescheid);
+			return (EntityModel<Bescheid>) linkedResourceProcessor.process(assembler.toModel(bescheid));
 		}
 	}
 
@@ -276,7 +282,7 @@ class BescheidModelAssemblerTest {
 
 		@Test
 		void shouldHaveEntityModel() {
-			var entityModel = ModelBuilder.fromEntity(bescheid).buildModel();
+			var entityModel = EntityModel.of(bescheid);
 			doReturn(entityModel).when(assembler).toModel(bescheid);
 
 			var collectionModel = callMethod();
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java
index 162e1cd996..5c9580a350 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java
@@ -23,13 +23,11 @@
  */
 package de.ozgcloud.alfa.bescheid;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import java.util.Optional;
-import java.util.function.BooleanSupplier;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -44,7 +42,6 @@ import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
 
 import de.ozgcloud.alfa.common.EntityModelTestFactory;
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.user.CurrentUserService;
 import de.ozgcloud.alfa.common.user.UserRole;
 import de.ozgcloud.alfa.vorgang.Vorgang;
@@ -87,7 +84,6 @@ class BescheidVorgangProcessorTest {
 
 		@Test
 		void shouldCallExistsBescheid() {
-			initUserProfileUrlProvider(new UserProfileUrlProvider());
 			doReturn(true).when(processor).existsBescheid(anyString());
 
 			callProcess();
@@ -98,11 +94,6 @@ class BescheidVorgangProcessorTest {
 		@Nested
 		class TestLinks {
 
-			@BeforeEach
-			void init() {
-				initUserProfileUrlProvider(new UserProfileUrlProvider());
-			}
-
 			@Test
 			void shouldNotHaveLinkToDraft() {
 				givenRetrievingDraftIsAllowed(false);
@@ -185,11 +176,11 @@ class BescheidVorgangProcessorTest {
 			}
 
 			private void givenRetrievingDraftIsAllowed(boolean shouldBeAdded) {
-				doReturn((BooleanSupplier) () -> shouldBeAdded).when(processor).isRetrievingDraftAllowed(vorgang);
+				doReturn(shouldBeAdded).when(processor).isRetrievingDraftAllowed(vorgang);
 			}
 
 			private void givenCreatingDraftIsAllowed(boolean shouldBeAdded) {
-				doReturn((BooleanSupplier) () -> shouldBeAdded).when(processor).isCreatingDraftAllowed(vorgang);
+				doReturn(shouldBeAdded).when(processor).isCreatingDraftAllowed(vorgang);
 			}
 		}
 
@@ -230,18 +221,18 @@ class BescheidVorgangProcessorTest {
 			givenVorgangInBearbeitung(true);
 			givenDraftExists(true);
 
-			var booleanSupplier = callMethod();
+			var result = callMethod();
 
-			assertThat(booleanSupplier.getAsBoolean()).isTrue();
+			assertThat(result).isTrue();
 		}
 
 		@Test
 		void shouldReturnFalseIfVorgangNotInBearbeitung() {
 			givenVorgangInBearbeitung(false);
 
-			var booleanSupplier = callMethod();
+			var result = callMethod();
 
-			assertThat(booleanSupplier.getAsBoolean()).isFalse();
+			assertThat(result).isFalse();
 		}
 
 		@Test
@@ -249,12 +240,12 @@ class BescheidVorgangProcessorTest {
 			givenVorgangInBearbeitung(true);
 			givenDraftExists(false);
 
-			var booleanSupplier = callMethod();
+			var result = callMethod();
 
-			assertThat(booleanSupplier.getAsBoolean()).isFalse();
+			assertThat(result).isFalse();
 		}
 
-		private BooleanSupplier callMethod() {
+		private boolean callMethod() {
 			return processor.isRetrievingDraftAllowed(vorgang);
 		}
 	}
@@ -267,18 +258,18 @@ class BescheidVorgangProcessorTest {
 			givenVorgangInBearbeitung(true);
 			givenDraftExists(false);
 
-			var booleanSupplier = callMethod();
+			var result = callMethod();
 
-			assertThat(booleanSupplier.getAsBoolean()).isTrue();
+			assertThat(result).isTrue();
 		}
 
 		@Test
 		void shouldReturnFalseIfVorgangNotInBearbeitung() {
 			givenVorgangInBearbeitung(false);
 
-			var booleanSupplier = callMethod();
+			var result = callMethod();
 
-			assertThat(booleanSupplier.getAsBoolean()).isFalse();
+			assertThat(result).isFalse();
 		}
 
 		@Test
@@ -286,12 +277,12 @@ class BescheidVorgangProcessorTest {
 			givenVorgangInBearbeitung(true);
 			givenDraftExists(true);
 
-			var booleanSupplier = callMethod();
+			var result = callMethod();
 
-			assertThat(booleanSupplier.getAsBoolean()).isFalse();
+			assertThat(result).isFalse();
 		}
 
-		private BooleanSupplier callMethod() {
+		private boolean callMethod() {
 			return processor.isCreatingDraftAllowed(vorgang);
 		}
 	}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/DocumentModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/DocumentModelAssemblerTest.java
index b867db73a6..559725a50c 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/DocumentModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/DocumentModelAssemblerTest.java
@@ -28,15 +28,20 @@ import static org.assertj.core.api.Assertions.*;
 
 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 de.ozgcloud.alfa.common.LinkedResourceProcessor;
+
 class DocumentModelAssemblerTest {
 
 	@Spy
 	private DocumentModelAssembler assembler;
+	@InjectMocks
+	private LinkedResourceProcessor linkedResourceProcessor;
 
 	@Nested
 	class TestToModel {
@@ -59,8 +64,9 @@ class DocumentModelAssemblerTest {
 					.isEqualTo("/api/binaryFiles/" + DocumentTestFactory.FILE_ID);
 		}
 
+		@SuppressWarnings("unchecked")
 		private EntityModel<Document> callToModel() {
-			return assembler.toModel(document);
+			return (EntityModel<Document>) linkedResourceProcessor.process(assembler.toModel(document));
 		}
 	}
 }
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java
index 4a64401a17..e275742843 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java
@@ -23,7 +23,6 @@
  */
 package de.ozgcloud.alfa.collaboration;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
@@ -94,11 +93,6 @@ class CollaborationVorgangProcessorTest {
 		@Nested
 		class OnNonNullVorgangAndOnVerwaltungUserRole {
 
-			@BeforeEach
-			void prepareBuilder() {
-				initUserProfileUrlProvider(urlProvider);
-			}
-
 			@BeforeEach
 			void mockUserService() {
 				when(currentUserService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(true);
@@ -113,10 +107,10 @@ class CollaborationVorgangProcessorTest {
 				}
 
 				@Test
-				void shouldHaveTwoLinks() {
+				void shouldHaveOneLink() {
 					var model = callProcessor();
 
-					assertThat(model.getLinks()).hasSize(2);
+					assertThat(model.getLinks()).hasSize(1);
 				}
 
 				@Test
@@ -148,7 +142,7 @@ class CollaborationVorgangProcessorTest {
 				void shouldHaveFourLinks() {
 					var model = callProcessor();
 
-					assertThat(model.getLinks()).hasSize(4);
+					assertThat(model.getLinks()).hasSize(3);
 				}
 
 				@Test
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/LinkedResourceProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/LinkedResourceProcessorTest.java
index c7953a7048..8262128113 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/LinkedResourceProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/LinkedResourceProcessorTest.java
@@ -35,7 +35,7 @@ class LinkedResourceProcessorTest {
 
 	@Spy
 	@InjectMocks
-	private LinkedResourceProcessor processor;
+	private LinkedResourceProcessor<TestEntity> processor;
 
 	@Mock
 	private UserManagerUrlProvider userManagerUrlProvider;
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/ModelBuilderTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/ModelBuilderTest.java
index 8b32ab1744..0c53bc9e49 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/ModelBuilderTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/ModelBuilderTest.java
@@ -24,11 +24,9 @@
 package de.ozgcloud.alfa.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;
@@ -47,11 +45,6 @@ class ModelBuilderTest {
 	@Nested
 	class TestAddLinkByAnnotationIfMissing {
 
-		private static final String USER_MANAGER_URL = "http://localhost";
-		private static final String USER_MANAGER_PROFILE_TEMPLATE = "/api/profile/%s";
-
-		private UserProfileUrlProvider provider = new UserProfileUrlProvider();
-
 		@Mock
 		private ApplicationContext context;
 		@Mock
@@ -59,15 +52,6 @@ class ModelBuilderTest {
 
 		private TestEntity entity = TestEntityTestFactory.create();
 
-		@BeforeEach
-		void mockEnvironment() {
-			when(env.getProperty(UserProfileUrlProvider.URL_ROOT_KEY)).thenReturn(USER_MANAGER_URL);
-			when(env.getProperty(UserProfileUrlProvider.USER_PROFILES_TEMPLATE_KEY)).thenReturn(USER_MANAGER_PROFILE_TEMPLATE);
-			when(context.getEnvironment()).thenReturn(env);
-
-			provider.setApplicationContext(context);
-		}
-
 		@Test
 		void shouldHaveAddLinkByLinkedResource() {
 			var model = ModelBuilder.fromEntity(entity).buildModel();
@@ -75,14 +59,6 @@ class ModelBuilderTest {
 			assertThat(model.getLink(TestController.FILE_REL).get().getHref()).isEqualTo(TestController.PATH + "/" + TestEntityTestFactory.FILE);
 		}
 
-		@Test
-		void shouldHaveAddLinkByLinkedUserProfileResource() {
-			var model = ModelBuilder.fromEntity(entity).buildModel();
-
-			assertThat(model.getLink(TestController.USER_REL).get().getHref())
-					.isEqualTo(String.format(USER_MANAGER_URL + USER_MANAGER_PROFILE_TEMPLATE, TestEntityTestFactory.USER));
-		}
-
 		@ParameterizedTest
 		@NullAndEmptySource
 		void shouldNotAddLinkByLinkedRessourceIfFieldValueIsNotSet(String fileId) {
@@ -96,36 +72,14 @@ class ModelBuilderTest {
 	@Nested
 	class TestNotAddLinkByAnnotationIfNotConfigured {
 
-		private UserProfileUrlProvider provider = new UserProfileUrlProvider();
-
-		@Mock
-		private ApplicationContext context;
-		@Mock
-		private Environment env;
-
 		private TestEntity entity = TestEntityTestFactory.create();
 
-		@BeforeEach
-		void mockEnvironment() {
-			when(env.getProperty(UserProfileUrlProvider.URL_ROOT_KEY)).thenReturn(null);
-			when(context.getEnvironment()).thenReturn(env);
-
-			provider.setApplicationContext(context);
-		}
-
 		@Test
 		void shouldHaveAddLinkByLinkedResource() {
 			var model = ModelBuilder.fromEntity(entity).buildModel();
 
 			assertThat(model.getLink(TestController.FILE_REL).get().getHref()).isEqualTo(TestController.PATH + "/" + TestEntityTestFactory.FILE);
 		}
-
-		@Test
-		void shouldNotHaveLinkAddByLinkedUserProfileAnnotation() {
-			var model = ModelBuilder.fromEntity(entity).buildModel();
-
-			assertThat(model.getLink(TestController.USER_REL)).isNotPresent();
-		}
 	}
 }
 
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java
index 756653dc95..d522973a92 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java
@@ -23,11 +23,9 @@
  */
 package de.ozgcloud.alfa.export;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -39,7 +37,6 @@ import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
 import org.springframework.hateoas.UriTemplate;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController;
 import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus;
 import de.ozgcloud.alfa.vorgang.VorgangController;
@@ -56,16 +53,9 @@ class ExportVorgangProcessorTest {
 	@Mock
 	private VorgangController vorgangController;
 
-	private final UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-
 	@Nested
 	class TestProcess {
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@DisplayName("Export link")
 		@Nested
 		class TestExportLink {
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieModelAssemblerTest.java
index 76c9affa1e..441e90c039 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieModelAssemblerTest.java
@@ -39,7 +39,8 @@ import org.springframework.hateoas.Link;
 
 import com.thedeanda.lorem.LoremIpsum;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
+import de.ozgcloud.alfa.common.LinkedResourceProcessor;
+import de.ozgcloud.alfa.common.command.Command;
 import de.ozgcloud.alfa.common.command.CommandTestFactory;
 import de.ozgcloud.alfa.common.user.UserId;
 import de.ozgcloud.alfa.common.user.UserManagerUrlProvider;
@@ -56,12 +57,8 @@ class HistorieModelAssemblerTest {
 
 	private static final String COMMAND_SINGLE_PATH = "/api/histories/" + CommandTestFactory.ID;
 
-	private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-
 	@Test
 	void shouldHaveSelfLink() {
-		initUserProfileUrlProvider(urlProvider);
-
 		var model = modelAssembler.toModel(CommandTestFactory.create());
 
 		assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH);
@@ -124,11 +121,17 @@ class HistorieModelAssemblerTest {
 	@DisplayName("createdBy Link")
 	@Nested
 	class TestCreatedByLink {
+
+		@InjectMocks
+		private LinkedResourceProcessor<Command> linkedResourceProcessor;
+		@Mock
+		private UserManagerUrlProvider userManagerUrlProvider;
+
 		@Test
 		void shouldExistingAtUser() {
-			initUserProfileUrlProvider(urlProvider);
+			when(userManagerUrlProvider.getUserProfileTemplate()).thenReturn(ROOT_URL + USER_PROFILES_API_PATH + "%s");
 
-			var model = modelAssembler.toModel(CommandTestFactory.create());
+			var model = linkedResourceProcessor.process(modelAssembler.toModel(CommandTestFactory.create()));
 
 			assertThat(model.getLink(CREATED_BY)).isPresent().get().extracting(Link::getHref)
 					.isEqualTo(ROOT_URL + USER_PROFILES_API_PATH + UserProfileTestFactory.ID);
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarModelAssemblerTest.java
index 882b6eab5e..91c487f0fc 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarModelAssemblerTest.java
@@ -41,8 +41,10 @@ import org.springframework.hateoas.IanaLinkRelations;
 import org.springframework.hateoas.Link;
 import org.springframework.hateoas.server.EntityLinks;
 
+import de.ozgcloud.alfa.common.LinkedResourceProcessor;
 import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory;
+import de.ozgcloud.alfa.common.user.UserManagerUrlProvider;
 import de.ozgcloud.alfa.vorgang.VorgangController;
 import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory;
 import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
@@ -97,6 +99,11 @@ class KommentarModelAssemblerTest {
 
 		private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create();
 
+		@InjectMocks
+		private LinkedResourceProcessor<Kommentar> linkedResourceProcessor;
+		@Mock
+		private UserManagerUrlProvider userManagerUrlProvider;
+
 		@Test
 		void shouldHaveSelfLink() {
 			var link = buildModel().getLink(IanaLinkRelations.SELF);
@@ -126,10 +133,9 @@ class KommentarModelAssemblerTest {
 
 		@Test
 		void shouldHaveCreatedByLink() {
-			UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-			initUserProfileUrlProvider(urlProvider);
+			when(userManagerUrlProvider.getUserProfileTemplate()).thenReturn(ROOT_URL + USER_PROFILES_API_PATH + "%s");
 
-			var model = buildModel();
+			var model = linkedResourceProcessor.process(buildModel());
 
 			assertThat(model.getLink(REL_CREATED_BY)).isPresent().get().extracting(Link::getHref)
 					.isEqualTo(USER_MANAGER_URL + KommentarTestFactory.CREATED_BY);
@@ -170,7 +176,6 @@ class KommentarModelAssemblerTest {
 
 		@Test
 		void shouldBuildModel() {
-			initUserProfileUrlProvider(urlProvider);
 			var kommentar = KommentarTestFactory.create();
 			var vorgang = VorgangWithEingangTestFactory.create();
 			when(vorgangController.getVorgang(VorgangHeaderTestFactory.ID)).thenReturn(vorgang);
@@ -182,8 +187,6 @@ class KommentarModelAssemblerTest {
 
 		@Test
 		void shouldLoadVorgang() {
-			initUserProfileUrlProvider(urlProvider);
-
 			modelAssembler.toCollectionModel(Collections.singleton(KommentarTestFactory.create()).stream(), VorgangHeaderTestFactory.ID);
 
 			verify(vorgangController).getVorgang(VorgangHeaderTestFactory.ID);
@@ -191,7 +194,6 @@ class KommentarModelAssemblerTest {
 
 		@Test
 		void shouldHaveCreateKommentarLink() {
-			initUserProfileUrlProvider(urlProvider);
 			when(vorgangController.isEditable(vorgang)).thenReturn(true);
 
 			var collectionModel = modelAssembler.toCollectionModel(Collections.singleton(KommentarTestFactory.create()).stream(),
@@ -206,7 +208,6 @@ class KommentarModelAssemblerTest {
 
 		@Test
 		void shouldNotHaveCreateKommentarLink() {
-			initUserProfileUrlProvider(urlProvider);
 			when(vorgangController.isEditable(vorgang)).thenReturn(false);
 
 			var collectionModel = modelAssembler.toCollectionModel(Collections.singleton(KommentarTestFactory.create()).stream(),
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandProcessorTest.java
index e2024c879a..384aadb5cd 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandProcessorTest.java
@@ -42,8 +42,6 @@ import org.mockito.Spy;
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
-import de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory;
 import de.ozgcloud.alfa.common.attacheditem.VorgangAttachedItemTestFactory;
 import de.ozgcloud.alfa.common.command.Command;
 import de.ozgcloud.alfa.common.command.CommandOrder;
@@ -63,11 +61,6 @@ class LoeschAnforderungCommandProcessorTest {
 	@Nested
 	class TestProcess {
 
-		@BeforeEach
-		void mock() {
-			UserProfileUrlProviderTestFactory.initUserProfileUrlProvider(new UserProfileUrlProvider());
-		}
-
 		@DisplayName("on revokeable")
 		@Nested
 		class TestRevokeableCommand {
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungModelAssemblerTest.java
index c6bedb3645..3d45e974f0 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungModelAssemblerTest.java
@@ -23,12 +23,10 @@
  */
 package de.ozgcloud.alfa.loeschanforderung;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -39,8 +37,6 @@ import org.springframework.hateoas.IanaLinkRelations;
 import org.springframework.hateoas.Link;
 import org.springframework.web.util.UriTemplate;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
-
 class LoeschAnforderungModelAssemblerTest {
 
 	@InjectMocks
@@ -48,11 +44,6 @@ class LoeschAnforderungModelAssemblerTest {
 	@Mock
 	private LoeschAnforderungService loeschAnforderungService;
 
-	@BeforeEach
-	void init() {
-		initUserProfileUrlProvider(new UserProfileUrlProvider());
-	}
-
 	@Nested
 	@DisplayName("Build resource")
 	class TestToModel {
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungVorgangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungVorgangProcessorTest.java
index a8ddd9361c..b332d2f57b 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungVorgangProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungVorgangProcessorTest.java
@@ -23,7 +23,6 @@
  */
 package de.ozgcloud.alfa.loeschanforderung;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
@@ -43,7 +42,6 @@ import org.mockito.Spy;
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.attacheditem.VorgangAttachedItemTestFactory;
 import de.ozgcloud.alfa.common.user.CurrentUserService;
 import de.ozgcloud.alfa.common.user.UserRole;
@@ -76,8 +74,6 @@ class LoeschAnforderungVorgangProcessorTest {
 
 			@BeforeEach
 			void mock() {
-				initUserProfileUrlProvider(new UserProfileUrlProvider());
-
 				when(loeschAnforderungService.findLoeschAnforderung(VorgangHeaderTestFactory.ID)).thenReturn(
 						Optional.of(VorgangAttachedItemTestFactory.create()));
 			}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangModelAssemblerTest.java
index 94ae793fbb..7b9dee67de 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangModelAssemblerTest.java
@@ -23,7 +23,6 @@
  */
 package de.ozgcloud.alfa.vorgang;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
@@ -33,7 +32,6 @@ import java.util.Optional;
 import java.util.stream.Stream;
 
 import org.apache.commons.lang3.StringUtils;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -44,7 +42,6 @@ import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.IanaLinkRelations;
 import org.springframework.hateoas.Link;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.user.CurrentUserService;
 import de.ozgcloud.alfa.common.user.UserProfileTestFactory;
 import de.ozgcloud.alfa.common.user.UserRole;
@@ -60,19 +57,12 @@ class VorgangModelAssemblerTest {
 	@Mock
 	private VorgangService vorgangService;
 
-	private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-
 	private static final int PAGE_SIZE = 100;
 
 	@DisplayName("To collectionModel")
 	@Nested
 	class TestCollectionModel {
 
-		@BeforeEach
-		void prepareBuilder() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@DisplayName("init model")
 		@Nested
 		class TestInitModel {
@@ -317,14 +307,8 @@ class VorgangModelAssemblerTest {
 	@Nested
 	class TestLinksOnModel {
 
-		private final UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
 		private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create();
 
-		@BeforeEach
-		void beforeEach() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@Test
 		void shouldHaveSelfLink() {
 			var link = toModel().getLink(IanaLinkRelations.SELF_VALUE);
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java
index 987b09227e..865bf45b36 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java
@@ -23,7 +23,6 @@
  */
 package de.ozgcloud.alfa.vorgang;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
@@ -42,7 +41,6 @@ import org.springframework.hateoas.LinkRelation;
 import org.springframework.web.util.UriComponentsBuilder;
 import org.springframework.web.util.UriTemplate;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController;
 import de.ozgcloud.alfa.common.user.UserManagerUrlProvider;
 import de.ozgcloud.alfa.postfach.PostfachMailController;
@@ -60,17 +58,10 @@ class VorgangWithEingangProcessorTest {
 	@Mock
 	private VorgangProcessorProperties vorgangProcessorProperties;
 
-	private final UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-
 	@DisplayName("Attachments")
 	@Nested
 	class TestAttachments {
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@DisplayName("link")
 		@Nested
 		class TestLink {
@@ -132,11 +123,6 @@ class VorgangWithEingangProcessorTest {
 	@Nested
 	class TestRepresentationsLink {
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		private static final String PATH = "/api/vorgang/" + VorgangHeaderTestFactory.ID + "/representations";
 
 		private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_REPRESENTATIONS;
@@ -214,11 +200,6 @@ class VorgangWithEingangProcessorTest {
 	@Nested
 	class TestSearchUserProfileLink {
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@DisplayName("on configured for search user profile")
 		@Nested
 		class TestIsConfigured {
@@ -287,11 +268,6 @@ class VorgangWithEingangProcessorTest {
 		private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create();
 		private final EntityModel<VorgangWithEingang> vorgangEntityModel = EntityModel.of(vorgang);
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@DisplayName("on matching configuration")
 		@Nested
 		class TestOnMatchingConfiguration {
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/command/VorgangWithEingangCommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/command/VorgangWithEingangCommandProcessorTest.java
index 37b3d3ad54..b99f8ab6a4 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/command/VorgangWithEingangCommandProcessorTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/command/VorgangWithEingangCommandProcessorTest.java
@@ -23,7 +23,6 @@
  */
 package de.ozgcloud.alfa.vorgang.command;
 
-import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
@@ -44,7 +43,6 @@ import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
 import org.springframework.hateoas.LinkRelation;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.command.CommandController;
 import de.ozgcloud.alfa.common.user.CurrentUserService;
 import de.ozgcloud.alfa.common.user.UserRole;
@@ -70,12 +68,8 @@ class VorgangWithEingangCommandProcessorTest {
 	private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create();
 	private EntityModel<VorgangWithEingang> vorgangEntityModel = EntityModel.of(vorgang);
 
-	private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-
 	@Test
 	void shouldReturnEntityModel() {
-		initUserProfileUrlProvider(urlProvider);
-
 		var result = processor.process(vorgangEntityModel);
 
 		assertThat(result).isNotNull();
@@ -155,11 +149,6 @@ class VorgangWithEingangCommandProcessorTest {
 
 		private final LinkRelation linkRel = VorgangWithEingangCommandProcessor.REL_VORGANG_FORWARD;
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@Test
 		void shouldNotBePresent() {
 			doReturn(false).when(processor).isForwardingAllowed(any());
@@ -186,11 +175,6 @@ class VorgangWithEingangCommandProcessorTest {
 	@Nested
 	class TestPendingCommandsLink {
 
-		@BeforeEach
-		void init() {
-			initUserProfileUrlProvider(urlProvider);
-		}
-
 		@Test
 		void shouldExists() {
 			when(commandController.existsPendingCommands(any())).thenReturn(true);
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssemblerTest.java
index 01cbb98f06..196c4aff1b 100644
--- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssemblerTest.java
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageModelAssemblerTest.java
@@ -24,7 +24,6 @@
 package de.ozgcloud.alfa.wiedervorlage;
 
 import static org.assertj.core.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import java.util.Collections;
@@ -36,13 +35,10 @@ import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.env.Environment;
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.IanaLinkRelations;
 import org.springframework.hateoas.server.EntityLinks;
 
-import de.ozgcloud.alfa.common.UserProfileUrlProvider;
 import de.ozgcloud.alfa.common.file.OzgFileTestFactory;
 import de.ozgcloud.alfa.vorgang.VorgangController;
 import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory;
@@ -51,7 +47,7 @@ import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory;
 
 class WiedervorlageModelAssemblerTest {
 
-	private final String PATH = WiedervorlageController.WIEDERVORLAGE_PATH + "/";
+	private static final String PATH = WiedervorlageController.WIEDERVORLAGE_PATH + "/";
 
 	@Spy
 	@InjectMocks
@@ -62,17 +58,6 @@ class WiedervorlageModelAssemblerTest {
 	@Mock
 	private VorgangController vorgangController;
 
-	private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider();
-
-	@BeforeEach
-	void initTest() {
-		var context = mock(ApplicationContext.class);
-		var environment = mock(Environment.class);
-		when(environment.getProperty(anyString())).thenReturn("test/");
-		when(context.getEnvironment()).thenReturn(environment);
-		urlProvider.setApplicationContext(context);
-	}
-
 	@Nested
 	class TestToModel {
 
-- 
GitLab