All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.dsf.fhir.dao.command.ReferencesHelperImpl Maven / Gradle / Ivy

package dev.dsf.fhir.dao.command;

import java.sql.Connection;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.hl7.fhir.r4.model.Attachment;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.RelatedArtifact;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.Type;

import dev.dsf.common.auth.conf.Identity;
import dev.dsf.fhir.help.ResponseGenerator;
import dev.dsf.fhir.service.ReferenceExtractor;
import dev.dsf.fhir.service.ReferenceResolver;
import dev.dsf.fhir.service.ResourceReference;
import dev.dsf.fhir.service.ResourceReference.ReferenceType;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;

public final class ReferencesHelperImpl implements ReferencesHelper
{
	private final int index;
	private final Identity identity;
	private final R resource;
	private final String serverBase;
	private final ReferenceExtractor referenceExtractor;
	private final ReferenceResolver referenceResolver;
	private final ResponseGenerator responseGenerator;

	public ReferencesHelperImpl(int index, Identity identity, R resource, String serverBase,
			ReferenceExtractor referenceExtractor, ReferenceResolver referenceResolver,
			ResponseGenerator responseGenerator)
	{
		this.index = index;
		this.identity = identity;
		this.resource = resource;
		this.serverBase = serverBase;
		this.referenceExtractor = referenceExtractor;
		this.referenceResolver = referenceResolver;
		this.responseGenerator = responseGenerator;
	}

	@Override
	public void resolveTemporaryAndConditionalReferencesOrLiteralInternalRelatedArtifactOrAttachmentUrls(
			Map idTranslationTable, Connection connection) throws WebApplicationException
	{
		referenceExtractor.getReferences(resource)
				.filter(ref -> referenceResolver.referenceCanBeResolved(ref, connection)).forEach(ref ->
				{
					Optional outcome = resolveTemporaryOrConditionalReferenceOrLiteralInternalRelatedArtifactOrAttachmentUrl(
							ref, idTranslationTable, connection);
					if (outcome.isPresent())
					{
						Response response = Response.status(Status.FORBIDDEN).entity(outcome.get()).build();
						throw new WebApplicationException(response);
					}
				});
	}

	private Optional resolveTemporaryOrConditionalReferenceOrLiteralInternalRelatedArtifactOrAttachmentUrl(
			ResourceReference reference, Map idTranslationTable, Connection connection)
	{
		return switch (reference.getType(serverBase))
		{
			case TEMPORARY -> resolveTemporary(reference, idTranslationTable, reference.getReference()::getReference,
					reference.getReference()::setReferenceElement);

			case RELATED_ARTEFACT_TEMPORARY_URL -> resolveTemporary(reference, idTranslationTable,
					reference.getRelatedArtifact()::getUrl, newIdToAbsoluteUrl(reference.getRelatedArtifact()::setUrl));

			case ATTACHMENT_TEMPORARY_URL -> resolveTemporary(reference, idTranslationTable,
					reference.getAttachment()::getUrl, newIdToAbsoluteUrl(reference.getAttachment()::setUrl));

			case CONDITIONAL ->
				resolveConditional(reference, connection, target -> reference.getReference().setReferenceElement(
						new IdType(target.getResourceType().name(), target.getIdElement().getIdPart())));

			case RELATED_ARTEFACT_CONDITIONAL_URL ->
				resolveConditional(reference, connection, targetToAbsoluteUrl(reference.getRelatedArtifact()::setUrl));

			case ATTACHMENT_CONDITIONAL_URL ->
				resolveConditional(reference, connection, targetToAbsoluteUrl(reference.getAttachment()::setUrl));

			case RELATED_ARTEFACT_LITERAL_INTERNAL_URL -> resolveLiteralInternalUrl(reference::getRelatedArtifact,
					RelatedArtifact::getUrl, RelatedArtifact::setUrl);

			case ATTACHMENT_LITERAL_INTERNAL_URL ->
				resolveLiteralInternalUrl(reference::getAttachment, Attachment::getUrl, Attachment::setUrl);

			default -> Optional.empty();
		};
	}

	private Consumer newIdToAbsoluteUrl(Consumer absoluteUrlConsumer)
	{
		return newId ->
		{
			String absoluteUrl = newId.withServerBase(serverBase, newId.getResourceType()).getValue();
			absoluteUrlConsumer.accept(absoluteUrl);
		};
	}

	private Consumer targetToAbsoluteUrl(Consumer absoluteUrlConsumer)
	{
		return target ->
		{
			IdType newId = new IdType(target.getResourceType().name(), target.getIdElement().getIdPart());
			String absoluteUrl = newId.withServerBase(serverBase, newId.getResourceType()).getValue();
			absoluteUrlConsumer.accept(absoluteUrl);
		};
	}

	private Optional resolveTemporary(ResourceReference reference,
			Map idTranslationTable, Supplier temporaryIdSupplier,
			Consumer newIdConsumer)
	{
		IdType newId = idTranslationTable.get(temporaryIdSupplier.get());
		if (newId != null)
		{
			newIdConsumer.accept(newId);
			return Optional.empty();
		}
		else
			return Optional.of(responseGenerator.unknownReference(resource, reference, index));
	}

	private Optional resolveConditional(ResourceReference reference, Connection connection,
			Consumer targetConsumer)
	{
		Optional resolvedResource = referenceResolver.resolveReference(identity, reference, connection);
		if (resolvedResource.isPresent())
		{
			Resource target = resolvedResource.get();
			targetConsumer.accept(target);

			return Optional.empty();
		}
		else
			return Optional.of(responseGenerator.referenceTargetNotFoundLocallyByCondition(index, resource, reference));
	}

	private  Optional resolveLiteralInternalUrl(Supplier element,
			Function oldUrlValue, BiConsumer newAbsoluteUrlConsumer)
	{
		T type = element.get();
		if (type != null)
		{
			IdType newId = new IdType(oldUrlValue.apply(type));
			String absoluteUrl = newId.withServerBase(serverBase, newId.getResourceType()).getValue();
			newAbsoluteUrlConsumer.accept(type, absoluteUrl);

			return Optional.empty();
		}

		return Optional.empty();
	}

	@Override
	public void resolveLogicalReferences(Connection connection) throws WebApplicationException
	{
		referenceExtractor.getReferences(resource).filter(ref -> ReferenceType.LOGICAL.equals(ref.getType(serverBase)))
				.filter(ref -> referenceResolver.referenceCanBeResolved(ref, connection)).forEach(ref ->
				{
					Optional outcome = resolveLogicalReference(ref, connection);
					if (outcome.isPresent())
					{
						Response response = Response.status(Status.FORBIDDEN).entity(outcome.get()).build();
						throw new WebApplicationException(response);
					}
				});
	}

	private Optional resolveLogicalReference(ResourceReference reference, Connection connection)
	{
		Optional resolvedResource = referenceResolver.resolveReference(identity, reference, connection);
		if (resolvedResource.isPresent())
		{
			Resource target = resolvedResource.get();
			reference.getReference().setReferenceElement(
					new IdType(target.getResourceType().name(), target.getIdElement().getIdPart()));

			return Optional.empty();
		}
		else
			return Optional
					.of(responseGenerator.referenceTargetNotFoundLocallyByIdentifier(index, resource, reference));
	}

	@Override
	public void checkReferences(Map idTranslationTable, Connection connection,
			Predicate checkReference) throws WebApplicationException
	{
		referenceExtractor.getReferences(resource).filter(checkReference)
				.filter(ref -> referenceResolver.referenceCanBeChecked(ref, connection)).forEach(ref ->
				{
					Optional outcome = checkReference(ref, connection);
					if (outcome.isPresent())
					{
						Response response = Response.status(Status.FORBIDDEN).entity(outcome.get()).build();
						throw new WebApplicationException(response);
					}
				});
	}

	private Optional checkReference(ResourceReference reference, Connection connection)
			throws WebApplicationException
	{
		return switch (reference.getType(serverBase))
		{
			case LITERAL_INTERNAL, RELATED_ARTEFACT_LITERAL_INTERNAL_URL, ATTACHMENT_LITERAL_INTERNAL_URL ->
				referenceResolver.checkLiteralInternalReference(resource, reference, connection, index);

			case LITERAL_EXTERNAL, RELATED_ARTEFACT_LITERAL_EXTERNAL_URL, ATTACHMENT_LITERAL_EXTERNAL_URL ->
				referenceResolver.checkLiteralExternalReference(resource, reference, index);

			case LOGICAL -> referenceResolver.checkLogicalReference(identity, resource, reference, connection, index);

			// unknown URLs to non FHIR servers in related artifacts must not be checked
			case RELATED_ARTEFACT_UNKNOWN_URL, ATTACHMENT_UNKNOWN_URL -> Optional.empty();

			case UNKNOWN -> Optional.of(responseGenerator.unknownReference(resource, reference, index));

			default -> Optional.of(responseGenerator.unknownReference(resource, reference, index));
		};
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy