Skip to content

Commit 6ed6d70

Browse files
committed
API v2 target provider impl and integration tests
1 parent 6663964 commit 6ed6d70

File tree

13 files changed

+698
-12
lines changed

13 files changed

+698
-12
lines changed

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/ProcessPluginApiImpl.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import dev.dsf.bpe.v2.service.OrganizationProvider;
1919
import dev.dsf.bpe.v2.service.QuestionnaireResponseHelper;
2020
import dev.dsf.bpe.v2.service.ReadAccessHelper;
21+
import dev.dsf.bpe.v2.service.TargetProvider;
2122
import dev.dsf.bpe.v2.service.TaskHelper;
2223
import dev.dsf.bpe.v2.service.process.ProcessAuthorizationHelper;
2324

@@ -39,14 +40,15 @@ public class ProcessPluginApiImpl implements ProcessPluginApi, InitializingBean
3940
private final ReadAccessHelper readAccessHelper;
4041
private final TaskHelper taskHelper;
4142
private final CryptoService cryptoService;
43+
private final TargetProvider targetProvider;
4244

4345
public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointProvider, FhirContext fhirContext,
4446
DsfClientProvider dsfClientProvider, FhirClientProvider fhirClientProvider,
4547
OidcClientProvider oidcClientProvider, MailService mailService, MimetypeService mimetypeService,
4648
ObjectMapper objectMapper, OrganizationProvider organizationProvider,
4749
ProcessAuthorizationHelper processAuthorizationHelper,
4850
QuestionnaireResponseHelper questionnaireResponseHelper, ReadAccessHelper readAccessHelper,
49-
TaskHelper taskHelper, CryptoService cryptoService)
51+
TaskHelper taskHelper, CryptoService cryptoService, TargetProvider targetProvider)
5052
{
5153
this.proxyConfig = proxyConfig;
5254
this.endpointProvider = endpointProvider;
@@ -63,6 +65,7 @@ public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointPr
6365
this.readAccessHelper = readAccessHelper;
6466
this.taskHelper = taskHelper;
6567
this.cryptoService = cryptoService;
68+
this.targetProvider = targetProvider;
6669
}
6770

6871
@Override
@@ -82,6 +85,7 @@ public void afterPropertiesSet() throws Exception
8285
Objects.requireNonNull(readAccessHelper, "readAccessHelper");
8386
Objects.requireNonNull(taskHelper, "taskHelper");
8487
Objects.requireNonNull(cryptoService, "cryptoService");
88+
Objects.requireNonNull(targetProvider, "targetProvider");
8589
}
8690

8791
@Override
@@ -173,4 +177,10 @@ public CryptoService getCryptoService()
173177
{
174178
return cryptoService;
175179
}
180+
181+
@Override
182+
public TargetProvider getTargetProvider()
183+
{
184+
return targetProvider;
185+
}
176186
}

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/AbstractResourceProvider.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import java.util.List;
88
import java.util.Map;
99
import java.util.Objects;
10+
import java.util.function.Function;
1011
import java.util.function.Predicate;
12+
import java.util.stream.Stream;
1113

1214
import org.hl7.fhir.r4.model.Bundle;
1315
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
@@ -49,23 +51,29 @@ protected final <R extends Resource> List<R> search(Class<? extends Resource> se
4951
Map<String, List<String>> searchParameters, SearchEntryMode targetMode, Class<R> targetType,
5052
Predicate<R> filter)
5153
{
52-
List<R> organizations = new ArrayList<>();
54+
return search(searchType, searchParameters).filter(e -> targetMode.equals(e.getSearch().getMode()))
55+
.filter(BundleEntryComponent::hasResource).map(BundleEntryComponent::getResource)
56+
.filter(targetType::isInstance).map(targetType::cast).filter(filter).toList();
57+
}
58+
59+
protected final Stream<BundleEntryComponent> search(Class<? extends Resource> searchType,
60+
Map<String, List<String>> searchParameters)
61+
{
62+
List<Stream<BundleEntryComponent>> resources = new ArrayList<>();
5363

5464
boolean hasMore = true;
5565
int page = 1;
5666
while (hasMore)
5767
{
5868
Bundle resultBundle = search(searchType, searchParameters, page++);
5969

60-
organizations.addAll(resultBundle.getEntry().stream().filter(BundleEntryComponent::hasSearch)
61-
.filter(e -> targetMode.equals(e.getSearch().getMode())).filter(BundleEntryComponent::hasResource)
62-
.map(BundleEntryComponent::getResource).filter(targetType::isInstance).map(targetType::cast)
63-
.filter(filter).toList());
70+
resources.add(resultBundle.getEntry().stream().filter(BundleEntryComponent::hasSearch)
71+
.filter(BundleEntryComponent::hasResource));
6472

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

68-
return organizations;
76+
return resources.stream().flatMap(Function.identity());
6977
}
7078

7179
private Bundle search(Class<? extends Resource> searchType, Map<String, List<String>> parameters, int page)
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package dev.dsf.bpe.v2.service;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.HashMap;
6+
import java.util.HashSet;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Objects;
10+
import java.util.Set;
11+
import java.util.UUID;
12+
import java.util.stream.Stream;
13+
14+
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
15+
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
16+
import org.hl7.fhir.r4.model.Coding;
17+
import org.hl7.fhir.r4.model.Endpoint;
18+
import org.hl7.fhir.r4.model.Identifier;
19+
import org.hl7.fhir.r4.model.Organization;
20+
import org.hl7.fhir.r4.model.OrganizationAffiliation;
21+
22+
import dev.dsf.bpe.v2.constants.NamingSystems.EndpointIdentifier;
23+
import dev.dsf.bpe.v2.constants.NamingSystems.OrganizationIdentifier;
24+
import dev.dsf.bpe.v2.variables.Target;
25+
import dev.dsf.bpe.v2.variables.TargetImpl;
26+
import dev.dsf.bpe.v2.variables.Targets;
27+
import dev.dsf.bpe.v2.variables.TargetsImpl;
28+
29+
public class TargetProviderImpl extends AbstractResourceProvider implements TargetProvider
30+
{
31+
public static class BuilderImpl implements Builder
32+
{
33+
private static final record OrganizationAffiliationAndOrganizationAndEndpoint(
34+
OrganizationAffiliation affiliation, Organization member, Endpoint endpoint)
35+
{
36+
}
37+
38+
private final List<OrganizationAffiliation> affiliations = new ArrayList<>();
39+
private final Map<String, Organization> organizationsById = new HashMap<>();
40+
private final Map<String, Endpoint> endpointsById = new HashMap<>();
41+
42+
private final Set<String> memberOrganizationIdentifiers = new HashSet<>();
43+
44+
public BuilderImpl(Identifier[] memberOrganizationIdentifiers)
45+
{
46+
if (memberOrganizationIdentifiers != null)
47+
this.memberOrganizationIdentifiers.addAll(Arrays.stream(memberOrganizationIdentifiers)
48+
.filter(Objects::nonNull).map(this::identifierToString).toList());
49+
}
50+
51+
private String identifierToString(Identifier i)
52+
{
53+
return i.getSystem() + "|" + i.getValue();
54+
}
55+
56+
private Predicate filter;
57+
58+
protected Target createTarget(String organizationIdentifierValue, String endpointIdentifierValue,
59+
String endpointAddress, String correlationKey)
60+
{
61+
Objects.requireNonNull(organizationIdentifierValue, "organizationIdentifierValue");
62+
Objects.requireNonNull(endpointIdentifierValue, "endpointIdentifierValue");
63+
Objects.requireNonNull(endpointAddress, "endpointAddress");
64+
65+
return new TargetImpl(organizationIdentifierValue, endpointIdentifierValue, endpointAddress,
66+
correlationKey);
67+
}
68+
69+
@Override
70+
public Targets withCorrelationKey()
71+
{
72+
return toTargets(true);
73+
}
74+
75+
@Override
76+
public Targets withoutCorrelationKey()
77+
{
78+
return toTargets(false);
79+
}
80+
81+
private Targets toTargets(boolean withCorrelationKey)
82+
{
83+
List<TargetImpl> targets = affiliations.stream()
84+
.filter(a -> organizationsById
85+
.containsKey(a.getParticipatingOrganization().getReferenceElement().getIdPart())
86+
&& endpointsById.containsKey(a.getEndpointFirstRep().getReferenceElement().getIdPart()))
87+
.map(a -> new OrganizationAffiliationAndOrganizationAndEndpoint(a,
88+
organizationsById.get(a.getParticipatingOrganization().getReferenceElement().getIdPart()),
89+
endpointsById.get(a.getEndpointFirstRep().getReferenceElement().getIdPart())))
90+
.filter(a -> OrganizationIdentifier.hasIdentifier(a.member)
91+
&& EndpointIdentifier.hasIdentifier(a.endpoint) && a.endpoint.hasAddressElement()
92+
&& a.endpoint.getAddressElement().hasValue())
93+
.filter(a -> memberOrganizationIdentifiers.isEmpty() ? true
94+
: memberOrganizationIdentifiers.contains(
95+
OrganizationIdentifier.findFirst(a.member).map(this::identifierToString).get()))
96+
.filter(a -> filter == null ? true : filter.test(a.affiliation, a.member, a.endpoint)).map(a ->
97+
{
98+
String organizationIdentifierValue = OrganizationIdentifier.findFirst(a.member)
99+
.map(Identifier::getValue).get();
100+
String endpointIdentifierValue = EndpointIdentifier.findFirst(a.endpoint)
101+
.map(Identifier::getValue).get();
102+
103+
return new TargetImpl(organizationIdentifierValue, endpointIdentifierValue,
104+
a.endpoint.getAddress(), withCorrelationKey ? UUID.randomUUID().toString() : null);
105+
}).toList();
106+
107+
return new TargetsImpl(targets);
108+
}
109+
110+
@Override
111+
public Builder filter(Predicate filter)
112+
{
113+
this.filter = filter;
114+
115+
return this;
116+
}
117+
}
118+
119+
public TargetProviderImpl(DsfClientProvider clientProvider, String localEndpointAddress)
120+
{
121+
super(clientProvider, localEndpointAddress);
122+
}
123+
124+
protected BuilderImpl createBuilder(Identifier... memberOrganizationIdentifier)
125+
{
126+
return new BuilderImpl(memberOrganizationIdentifier);
127+
}
128+
129+
private Builder toBuilder(Stream<BundleEntryComponent> entries, Identifier... memberOrganizationIdentifier)
130+
{
131+
BuilderImpl builder = createBuilder(memberOrganizationIdentifier);
132+
133+
entries.forEach(c ->
134+
{
135+
SearchEntryMode mode = c.getSearch().getMode();
136+
137+
if (SearchEntryMode.MATCH.equals(mode) && c.getResource() instanceof OrganizationAffiliation a)
138+
builder.affiliations.add(a);
139+
140+
else if (SearchEntryMode.INCLUDE.equals(mode))
141+
{
142+
if (c.getResource() instanceof Organization o)
143+
builder.organizationsById.put(o.getIdElement().getIdPart(), o);
144+
else if (c.getResource() instanceof Endpoint e)
145+
builder.endpointsById.put(e.getIdElement().getIdPart(), e);
146+
}
147+
});
148+
149+
return builder;
150+
}
151+
152+
@Override
153+
public Builder create(Identifier parentOrganizationIdentifier)
154+
{
155+
Objects.requireNonNull(parentOrganizationIdentifier, "parentOrganizationIdentifier");
156+
157+
Stream<BundleEntryComponent> entries = search(OrganizationAffiliation.class,
158+
Map.of("active", List.of("true"), "primary-organization:identifier",
159+
List.of(toSearchParameter(parentOrganizationIdentifier)), "_include",
160+
List.of("OrganizationAffiliation:endpoint:Endpoint",
161+
"OrganizationAffiliation:participating-organization:Organization")));
162+
163+
return toBuilder(entries);
164+
}
165+
166+
@Override
167+
public Builder create(Identifier parentOrganizationIdentifier, Coding memberOrganizationRole)
168+
{
169+
Objects.requireNonNull(parentOrganizationIdentifier, "parentOrganizationIdentifier");
170+
Objects.requireNonNull(memberOrganizationRole, "memberOrganizationRole");
171+
172+
Stream<BundleEntryComponent> entries = search(OrganizationAffiliation.class,
173+
Map.of("active", List.of("true"), "primary-organization:identifier",
174+
List.of(toSearchParameter(parentOrganizationIdentifier)), "_include",
175+
List.of("OrganizationAffiliation:endpoint:Endpoint",
176+
"OrganizationAffiliation:participating-organization:Organization"),
177+
"role", List.of(toSearchParameter(memberOrganizationRole))));
178+
179+
return toBuilder(entries);
180+
}
181+
182+
@Override
183+
public Builder create(Identifier parentOrganizationIdentifier, Coding memberOrganizationRole,
184+
Identifier... memberOrganizationIdentifier)
185+
{
186+
Objects.requireNonNull(parentOrganizationIdentifier, "parentOrganizationIdentifier");
187+
Objects.requireNonNull(memberOrganizationRole, "memberOrganizationRole");
188+
Objects.requireNonNull(memberOrganizationIdentifier, "memberOrganizationIdentifier");
189+
190+
Stream<BundleEntryComponent> entries = search(OrganizationAffiliation.class,
191+
Map.of("active", List.of("true"), "primary-organization:identifier",
192+
List.of(toSearchParameter(parentOrganizationIdentifier)), "_include",
193+
List.of("OrganizationAffiliation:endpoint:Endpoint",
194+
"OrganizationAffiliation:participating-organization:Organization"),
195+
"role", List.of(toSearchParameter(memberOrganizationRole))));
196+
197+
return toBuilder(entries, memberOrganizationIdentifier);
198+
}
199+
}

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/spring/ApiServiceConfig.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import dev.dsf.bpe.v2.service.QuestionnaireResponseHelperImpl;
5959
import dev.dsf.bpe.v2.service.ReadAccessHelper;
6060
import dev.dsf.bpe.v2.service.ReadAccessHelperImpl;
61+
import dev.dsf.bpe.v2.service.TargetProvider;
62+
import dev.dsf.bpe.v2.service.TargetProviderImpl;
6163
import dev.dsf.bpe.v2.service.TaskHelper;
6264
import dev.dsf.bpe.v2.service.TaskHelperImpl;
6365
import dev.dsf.bpe.v2.service.detector.CombinedDetectors;
@@ -99,7 +101,7 @@ public ProcessPluginApi processPluginApiV2()
99101
return new ProcessPluginApiImpl(proxyConfigDelegate(), endpointProvider(), fhirContext(), dsfClientProvider(),
100102
fhirClientProvider(), oidcClientProvider(), mailService(), mimetypeService(), objectMapper(),
101103
organizationProvider(), processAuthorizationHelper(), questionnaireResponseHelper(), readAccessHelper(),
102-
taskHelper(), cryptoService());
104+
taskHelper(), cryptoService(), targetProvider());
103105
}
104106

105107
@Bean
@@ -285,4 +287,10 @@ public CryptoService cryptoService()
285287
{
286288
return new CryptoServiceImpl();
287289
}
290+
291+
@Bean
292+
public TargetProvider targetProvider()
293+
{
294+
return new TargetProviderImpl(dsfClientProvider(), dsfClientConfig.getLocalConfig().getBaseUrl());
295+
}
288296
}

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/variables/TargetsImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ public boolean isEmpty()
6666
return entries.isEmpty();
6767
}
6868

69+
@Override
70+
public int size()
71+
{
72+
return entries.size();
73+
}
74+
6975
@Override
7076
public String toString()
7177
{

dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginApi.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import dev.dsf.bpe.v2.service.OrganizationProvider;
1818
import dev.dsf.bpe.v2.service.QuestionnaireResponseHelper;
1919
import dev.dsf.bpe.v2.service.ReadAccessHelper;
20+
import dev.dsf.bpe.v2.service.TargetProvider;
2021
import dev.dsf.bpe.v2.service.TaskHelper;
2122
import dev.dsf.bpe.v2.service.process.ProcessAuthorizationHelper;
2223
import dev.dsf.bpe.v2.variables.Variables;
@@ -58,4 +59,6 @@ public interface ProcessPluginApi
5859
TaskHelper getTaskHelper();
5960

6061
CryptoService getCryptoService();
62+
63+
TargetProvider getTargetProvider();
6164
}

0 commit comments

Comments
 (0)