Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import dev.dsf.bpe.v2.service.OrganizationProvider;
import dev.dsf.bpe.v2.service.QuestionnaireResponseHelper;
import dev.dsf.bpe.v2.service.ReadAccessHelper;
import dev.dsf.bpe.v2.service.TargetProvider;
import dev.dsf.bpe.v2.service.TaskHelper;
import dev.dsf.bpe.v2.service.process.ProcessAuthorizationHelper;

Expand All @@ -39,14 +40,15 @@ public class ProcessPluginApiImpl implements ProcessPluginApi, InitializingBean
private final ReadAccessHelper readAccessHelper;
private final TaskHelper taskHelper;
private final CryptoService cryptoService;
private final TargetProvider targetProvider;

public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointProvider, FhirContext fhirContext,
DsfClientProvider dsfClientProvider, FhirClientProvider fhirClientProvider,
OidcClientProvider oidcClientProvider, MailService mailService, MimetypeService mimetypeService,
ObjectMapper objectMapper, OrganizationProvider organizationProvider,
ProcessAuthorizationHelper processAuthorizationHelper,
QuestionnaireResponseHelper questionnaireResponseHelper, ReadAccessHelper readAccessHelper,
TaskHelper taskHelper, CryptoService cryptoService)
TaskHelper taskHelper, CryptoService cryptoService, TargetProvider targetProvider)
{
this.proxyConfig = proxyConfig;
this.endpointProvider = endpointProvider;
Expand All @@ -63,6 +65,7 @@ public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointPr
this.readAccessHelper = readAccessHelper;
this.taskHelper = taskHelper;
this.cryptoService = cryptoService;
this.targetProvider = targetProvider;
}

@Override
Expand All @@ -82,6 +85,7 @@ public void afterPropertiesSet() throws Exception
Objects.requireNonNull(readAccessHelper, "readAccessHelper");
Objects.requireNonNull(taskHelper, "taskHelper");
Objects.requireNonNull(cryptoService, "cryptoService");
Objects.requireNonNull(targetProvider, "targetProvider");
}

@Override
Expand Down Expand Up @@ -173,4 +177,10 @@ public CryptoService getCryptoService()
{
return cryptoService;
}

@Override
public TargetProvider getTargetProvider()
{
return targetProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
Expand Down Expand Up @@ -49,23 +51,29 @@ protected final <R extends Resource> List<R> search(Class<? extends Resource> se
Map<String, List<String>> searchParameters, SearchEntryMode targetMode, Class<R> targetType,
Predicate<R> filter)
{
List<R> organizations = new ArrayList<>();
return search(searchType, searchParameters).filter(e -> targetMode.equals(e.getSearch().getMode()))
.filter(BundleEntryComponent::hasResource).map(BundleEntryComponent::getResource)
.filter(targetType::isInstance).map(targetType::cast).filter(filter).toList();
}

protected final Stream<BundleEntryComponent> search(Class<? extends Resource> searchType,
Map<String, List<String>> searchParameters)
{
List<Stream<BundleEntryComponent>> resources = new ArrayList<>();

boolean hasMore = true;
int page = 1;
while (hasMore)
{
Bundle resultBundle = search(searchType, searchParameters, page++);

organizations.addAll(resultBundle.getEntry().stream().filter(BundleEntryComponent::hasSearch)
.filter(e -> targetMode.equals(e.getSearch().getMode())).filter(BundleEntryComponent::hasResource)
.map(BundleEntryComponent::getResource).filter(targetType::isInstance).map(targetType::cast)
.filter(filter).toList());
resources.add(resultBundle.getEntry().stream().filter(BundleEntryComponent::hasSearch)
.filter(BundleEntryComponent::hasResource));

hasMore = resultBundle.getLink(LINK_NEXT) != null;
}

return organizations;
return resources.stream().flatMap(Function.identity());
}

private Bundle search(Class<? extends Resource> searchType, Map<String, List<String>> parameters, int page)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package dev.dsf.bpe.v2.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;

import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Endpoint;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.OrganizationAffiliation;

import dev.dsf.bpe.v2.constants.NamingSystems.EndpointIdentifier;
import dev.dsf.bpe.v2.constants.NamingSystems.OrganizationIdentifier;
import dev.dsf.bpe.v2.variables.Target;
import dev.dsf.bpe.v2.variables.TargetImpl;
import dev.dsf.bpe.v2.variables.Targets;
import dev.dsf.bpe.v2.variables.TargetsImpl;

public class TargetProviderImpl extends AbstractResourceProvider implements TargetProvider
{
public static class BuilderImpl implements Builder
{
private static final record OrganizationAffiliationAndOrganizationAndEndpoint(
OrganizationAffiliation affiliation, Organization member, Endpoint endpoint)
{
}

private final List<OrganizationAffiliation> affiliations = new ArrayList<>();
private final Map<String, Organization> organizationsById = new HashMap<>();
private final Map<String, Endpoint> endpointsById = new HashMap<>();

private final Set<String> memberOrganizationIdentifiers = new HashSet<>();

public BuilderImpl(Identifier[] memberOrganizationIdentifiers)
{
if (memberOrganizationIdentifiers != null)
this.memberOrganizationIdentifiers.addAll(Arrays.stream(memberOrganizationIdentifiers)
.filter(Objects::nonNull).map(this::identifierToString).toList());
}

private String identifierToString(Identifier i)
{
return i.getSystem() + "|" + i.getValue();
}

private Predicate filter;

protected Target createTarget(String organizationIdentifierValue, String endpointIdentifierValue,
String endpointAddress, String correlationKey)
{
Objects.requireNonNull(organizationIdentifierValue, "organizationIdentifierValue");
Objects.requireNonNull(endpointIdentifierValue, "endpointIdentifierValue");
Objects.requireNonNull(endpointAddress, "endpointAddress");

return new TargetImpl(organizationIdentifierValue, endpointIdentifierValue, endpointAddress,
correlationKey);
}

@Override
public Targets withCorrelationKey()
{
return toTargets(true);
}

@Override
public Targets withoutCorrelationKey()
{
return toTargets(false);
}

private Targets toTargets(boolean withCorrelationKey)
{
List<TargetImpl> targets = affiliations.stream()
.filter(a -> organizationsById
.containsKey(a.getParticipatingOrganization().getReferenceElement().getIdPart())
&& endpointsById.containsKey(a.getEndpointFirstRep().getReferenceElement().getIdPart()))
.map(a -> new OrganizationAffiliationAndOrganizationAndEndpoint(a,
organizationsById.get(a.getParticipatingOrganization().getReferenceElement().getIdPart()),
endpointsById.get(a.getEndpointFirstRep().getReferenceElement().getIdPart())))
.filter(a -> OrganizationIdentifier.hasIdentifier(a.member)
&& EndpointIdentifier.hasIdentifier(a.endpoint) && a.endpoint.hasAddressElement()
&& a.endpoint.getAddressElement().hasValue())
.filter(a -> memberOrganizationIdentifiers.isEmpty() ? true
: memberOrganizationIdentifiers.contains(
OrganizationIdentifier.findFirst(a.member).map(this::identifierToString).get()))
.filter(a -> filter == null ? true : filter.test(a.affiliation, a.member, a.endpoint)).map(a ->
{
String organizationIdentifierValue = OrganizationIdentifier.findFirst(a.member)
.map(Identifier::getValue).get();
String endpointIdentifierValue = EndpointIdentifier.findFirst(a.endpoint)
.map(Identifier::getValue).get();

return new TargetImpl(organizationIdentifierValue, endpointIdentifierValue,
a.endpoint.getAddress(), withCorrelationKey ? UUID.randomUUID().toString() : null);
}).toList();

return new TargetsImpl(targets);
}

@Override
public Builder filter(Predicate filter)
{
this.filter = filter;

return this;
}
}

public TargetProviderImpl(DsfClientProvider clientProvider, String localEndpointAddress)
{
super(clientProvider, localEndpointAddress);
}

protected BuilderImpl createBuilder(Identifier... memberOrganizationIdentifier)
{
return new BuilderImpl(memberOrganizationIdentifier);
}

private Builder toBuilder(Stream<BundleEntryComponent> entries, Identifier... memberOrganizationIdentifier)
{
BuilderImpl builder = createBuilder(memberOrganizationIdentifier);

entries.forEach(c ->
{
SearchEntryMode mode = c.getSearch().getMode();

if (SearchEntryMode.MATCH.equals(mode) && c.getResource() instanceof OrganizationAffiliation a)
builder.affiliations.add(a);

else if (SearchEntryMode.INCLUDE.equals(mode))
{
if (c.getResource() instanceof Organization o)
builder.organizationsById.put(o.getIdElement().getIdPart(), o);
else if (c.getResource() instanceof Endpoint e)
builder.endpointsById.put(e.getIdElement().getIdPart(), e);
}
});

return builder;
}

@Override
public Builder create(Identifier parentOrganizationIdentifier)
{
Objects.requireNonNull(parentOrganizationIdentifier, "parentOrganizationIdentifier");

Stream<BundleEntryComponent> entries = search(OrganizationAffiliation.class,
Map.of("active", List.of("true"), "primary-organization:identifier",
List.of(toSearchParameter(parentOrganizationIdentifier)), "_include",
List.of("OrganizationAffiliation:endpoint:Endpoint",
"OrganizationAffiliation:participating-organization:Organization")));

return toBuilder(entries);
}

@Override
public Builder create(Identifier parentOrganizationIdentifier, Coding memberOrganizationRole)
{
Objects.requireNonNull(parentOrganizationIdentifier, "parentOrganizationIdentifier");
Objects.requireNonNull(memberOrganizationRole, "memberOrganizationRole");

Stream<BundleEntryComponent> entries = search(OrganizationAffiliation.class,
Map.of("active", List.of("true"), "primary-organization:identifier",
List.of(toSearchParameter(parentOrganizationIdentifier)), "_include",
List.of("OrganizationAffiliation:endpoint:Endpoint",
"OrganizationAffiliation:participating-organization:Organization"),
"role", List.of(toSearchParameter(memberOrganizationRole))));

return toBuilder(entries);
}

@Override
public Builder create(Identifier parentOrganizationIdentifier, Coding memberOrganizationRole,
Identifier... memberOrganizationIdentifier)
{
Objects.requireNonNull(parentOrganizationIdentifier, "parentOrganizationIdentifier");
Objects.requireNonNull(memberOrganizationRole, "memberOrganizationRole");
Objects.requireNonNull(memberOrganizationIdentifier, "memberOrganizationIdentifier");

Stream<BundleEntryComponent> entries = search(OrganizationAffiliation.class,
Map.of("active", List.of("true"), "primary-organization:identifier",
List.of(toSearchParameter(parentOrganizationIdentifier)), "_include",
List.of("OrganizationAffiliation:endpoint:Endpoint",
"OrganizationAffiliation:participating-organization:Organization"),
"role", List.of(toSearchParameter(memberOrganizationRole))));

return toBuilder(entries, memberOrganizationIdentifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
import dev.dsf.bpe.v2.service.QuestionnaireResponseHelperImpl;
import dev.dsf.bpe.v2.service.ReadAccessHelper;
import dev.dsf.bpe.v2.service.ReadAccessHelperImpl;
import dev.dsf.bpe.v2.service.TargetProvider;
import dev.dsf.bpe.v2.service.TargetProviderImpl;
import dev.dsf.bpe.v2.service.TaskHelper;
import dev.dsf.bpe.v2.service.TaskHelperImpl;
import dev.dsf.bpe.v2.service.detector.CombinedDetectors;
Expand Down Expand Up @@ -99,7 +101,7 @@ public ProcessPluginApi processPluginApiV2()
return new ProcessPluginApiImpl(proxyConfigDelegate(), endpointProvider(), fhirContext(), dsfClientProvider(),
fhirClientProvider(), oidcClientProvider(), mailService(), mimetypeService(), objectMapper(),
organizationProvider(), processAuthorizationHelper(), questionnaireResponseHelper(), readAccessHelper(),
taskHelper(), cryptoService());
taskHelper(), cryptoService(), targetProvider());
}

@Bean
Expand Down Expand Up @@ -285,4 +287,10 @@ public CryptoService cryptoService()
{
return new CryptoServiceImpl();
}

@Bean
public TargetProvider targetProvider()
{
return new TargetProviderImpl(dsfClientProvider(), dsfClientConfig.getLocalConfig().getBaseUrl());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonCreator;
Expand Down Expand Up @@ -66,6 +67,18 @@ public boolean isEmpty()
return entries.isEmpty();
}

@Override
public int size()
{
return entries.size();
}

@Override
public Optional<Target> getFirst()
{
return entries.isEmpty() ? Optional.empty() : Optional.of(entries.get(0));
}

@Override
public String toString()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import dev.dsf.bpe.v2.service.OrganizationProvider;
import dev.dsf.bpe.v2.service.QuestionnaireResponseHelper;
import dev.dsf.bpe.v2.service.ReadAccessHelper;
import dev.dsf.bpe.v2.service.TargetProvider;
import dev.dsf.bpe.v2.service.TaskHelper;
import dev.dsf.bpe.v2.service.process.ProcessAuthorizationHelper;
import dev.dsf.bpe.v2.variables.Variables;
Expand Down Expand Up @@ -58,4 +59,6 @@ public interface ProcessPluginApi
TaskHelper getTaskHelper();

CryptoService getCryptoService();

TargetProvider getTargetProvider();
}
Loading