Allow canonical subscription to hold multiple extensions with the same
URL
This commit is contained in:
parent
87ed00a002
commit
c6fcb4fcbf
|
@ -198,7 +198,7 @@ public class SubscriptionActivatingInterceptor {
|
|||
String criteria = mySubscriptionCanonicalizer.getCriteria(theResource);
|
||||
try {
|
||||
SubscriptionMatchingStrategy strategy = mySubscriptionStrategyEvaluator.determineStrategy(criteria);
|
||||
mySubscriptionCanonicalizer.setMatchingStrategyTag(myFhirContext, theResource, strategy);
|
||||
mySubscriptionCanonicalizer.setMatchingStrategyTag(theResource, strategy);
|
||||
} catch (InvalidRequestException | DataFormatException e) {
|
||||
throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + criteria + " " + e.getMessage());
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|||
import org.hl7.fhir.r4.model.Subscription;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -64,7 +65,7 @@ public class CanonicalSubscription implements Serializable, Cloneable {
|
|||
@JsonProperty("restHookDetails")
|
||||
private RestHookDetails myRestHookDetails;
|
||||
@JsonProperty("extensions")
|
||||
private Map<String, String> myChannelExtensions;
|
||||
private Map<String, List<String>> myChannelExtensions;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -134,15 +135,32 @@ public class CanonicalSubscription implements Serializable, Cloneable {
|
|||
}
|
||||
}
|
||||
|
||||
public String getChannelExtension(String url) {
|
||||
return myChannelExtensions.get(url);
|
||||
public String getChannelExtension(String theUrl) {
|
||||
String retVal = null;
|
||||
List<String> strings = myChannelExtensions.get(theUrl);
|
||||
if (strings != null && strings.isEmpty()==false) {
|
||||
retVal = strings.get(0);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void setChannelExtensions(Map<String, String> theChannelExtensions) {
|
||||
@Nonnull
|
||||
public List<String> getChannelExtensions(String theUrl) {
|
||||
List<String> retVal = myChannelExtensions.get(theUrl);
|
||||
if (retVal == null) {
|
||||
retVal = Collections.emptyList();
|
||||
} else {
|
||||
retVal = Collections.unmodifiableList(retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void setChannelExtensions(Map<String, List<String>> theChannelExtensions) {
|
||||
myChannelExtensions = new HashMap<>();
|
||||
for (String url : theChannelExtensions.keySet()) {
|
||||
if (isNotBlank(url) && isNotBlank(theChannelExtensions.get(url))) {
|
||||
myChannelExtensions.put(url, theChannelExtensions.get(url));
|
||||
List<String> values = theChannelExtensions.get(url);
|
||||
if (isNotBlank(url) && values != null) {
|
||||
myChannelExtensions.put(url, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
|
||||
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscriptionChannelType;
|
||||
import ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchingStrategy;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
@ -40,11 +38,14 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static java.util.stream.Collectors.mapping;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
@Service
|
||||
public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
||||
|
@ -61,6 +62,8 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
return canonicalizeDstu3(theSubscription);
|
||||
case R4:
|
||||
return canonicalizeR4(theSubscription);
|
||||
case DSTU2_HL7ORG:
|
||||
case DSTU2_1:
|
||||
default:
|
||||
throw new ConfigurationException("Subscription not supported for version: " + myFhirContext.getVersion().getVersion());
|
||||
}
|
||||
|
@ -76,7 +79,7 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
retVal.setCriteriaString(subscription.getCriteria());
|
||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
||||
retVal.setChannelExtensions(convertChannelExtensionsDstu2(subscription));
|
||||
retVal.setChannelExtensions(extractExtension(subscription));
|
||||
retVal.setIdElement(subscription.getIdElement());
|
||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
||||
} catch (FHIRException theE) {
|
||||
|
@ -95,7 +98,7 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
retVal.setCriteriaString(subscription.getCriteria());
|
||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
||||
retVal.setChannelExtensions(convertChannelExtensionsDstu3(subscription));
|
||||
retVal.setChannelExtensions(extractExtension(subscription));
|
||||
retVal.setIdElement(subscription.getIdElement());
|
||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
||||
|
||||
|
@ -133,85 +136,45 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private Map<String, String> convertChannelExtensionsDstu2(ca.uhn.fhir.model.dstu2.resource.Subscription theSubscription) {
|
||||
Map<String, String> retval = new HashMap<>();
|
||||
for (ExtensionDt extension : theSubscription.getChannel().getUndeclaredExtensions()) {
|
||||
String url = extension.getUrl();
|
||||
if (isNotBlank(url)) {
|
||||
String value = extractExtension(theSubscription, url);
|
||||
if (isNotBlank(value)) {
|
||||
retval.put(url, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
private Map<String, String> convertChannelExtensionsDstu3(org.hl7.fhir.dstu3.model.Subscription theSubscription) {
|
||||
Map<String, String> retval = new HashMap<>();
|
||||
for (org.hl7.fhir.dstu3.model.Extension extension : theSubscription.getChannel().getExtension()) {
|
||||
String url = extension.getUrl();
|
||||
if (isNotBlank(url)) {
|
||||
String value = extractExtension(theSubscription, url);
|
||||
if (isNotBlank(value)) {
|
||||
retval.put(url, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
private Map<String, String> convertChannelExtensionsR4(org.hl7.fhir.r4.model.Subscription theSubscription) {
|
||||
Map<String, String> retval = new HashMap<>();
|
||||
for (org.hl7.fhir.r4.model.Extension extension : theSubscription.getChannel().getExtension()) {
|
||||
String url = extension.getUrl();
|
||||
if (isNotBlank(url)) {
|
||||
String value = extractExtension(theSubscription, url);
|
||||
if (isNotBlank(value)) {
|
||||
retval.put(url, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
private String extractExtension(IBaseResource theSubscription, String theUrl) {
|
||||
private @Nonnull
|
||||
Map<String, List<String>> extractExtension(IBaseResource theSubscription) {
|
||||
try {
|
||||
switch (theSubscription.getStructureFhirVersionEnum()) {
|
||||
case DSTU2: {
|
||||
ca.uhn.fhir.model.dstu2.resource.Subscription subscription = (ca.uhn.fhir.model.dstu2.resource.Subscription) theSubscription;
|
||||
List<ExtensionDt> extensions = subscription.getChannel().getUndeclaredExtensionsByUrl(theUrl);
|
||||
if (extensions.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
if (extensions.size() > 1) {
|
||||
throw new FHIRException("Multiple matching extensions found");
|
||||
}
|
||||
if (!(extensions.get(0).getValue() instanceof IPrimitiveDatatype)) {
|
||||
throw new FHIRException("Extension could not be converted to a string");
|
||||
}
|
||||
return ((IPrimitiveDatatype<?>) extensions.get(0).getValue()).getValueAsString();
|
||||
return subscription
|
||||
.getChannel()
|
||||
.getUndeclaredExtensions()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||
}
|
||||
case DSTU3: {
|
||||
org.hl7.fhir.dstu3.model.Subscription subscription = (org.hl7.fhir.dstu3.model.Subscription) theSubscription;
|
||||
return subscription.getChannel().getExtensionString(theUrl);
|
||||
return subscription
|
||||
.getChannel()
|
||||
.getExtension()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||
}
|
||||
case R4: {
|
||||
org.hl7.fhir.r4.model.Subscription subscription = (org.hl7.fhir.r4.model.Subscription) theSubscription;
|
||||
return subscription.getChannel().getExtensionString(theUrl);
|
||||
return subscription
|
||||
.getChannel()
|
||||
.getExtension()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||
}
|
||||
case DSTU2_HL7ORG:
|
||||
case DSTU2_1:
|
||||
default: {
|
||||
ourLog.error("Failed to extract extension with URL {} from subscription {}", theUrl, theSubscription.getIdElement().toUnqualified().getValue());
|
||||
ourLog.error("Failed to extract extension from subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (FHIRException theE) {
|
||||
ourLog.error("Failed to extract extension with URL {} from subscription {}", theUrl, theSubscription.getIdElement().toUnqualified().getValue(), theE);
|
||||
ourLog.error("Failed to extract extension from subscription {}", theSubscription.getIdElement().toUnqualified().getValue(), theE);
|
||||
}
|
||||
return null;
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
private CanonicalSubscription canonicalizeR4(IBaseResource theSubscription) {
|
||||
|
@ -223,7 +186,7 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
retVal.setCriteriaString(subscription.getCriteria());
|
||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
||||
retVal.setChannelExtensions(convertChannelExtensionsR4(subscription));
|
||||
retVal.setChannelExtensions(extractExtension(subscription));
|
||||
retVal.setIdElement(subscription.getIdElement());
|
||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
||||
|
||||
|
@ -267,18 +230,20 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
public String getCriteria(IBaseResource theSubscription) {
|
||||
switch (myFhirContext.getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
return ((ca.uhn.fhir.model.dstu2.resource.Subscription)theSubscription).getCriteria();
|
||||
return ((ca.uhn.fhir.model.dstu2.resource.Subscription) theSubscription).getCriteria();
|
||||
case DSTU3:
|
||||
return ((org.hl7.fhir.dstu3.model.Subscription)theSubscription).getCriteria();
|
||||
return ((org.hl7.fhir.dstu3.model.Subscription) theSubscription).getCriteria();
|
||||
case R4:
|
||||
return ((org.hl7.fhir.r4.model.Subscription)theSubscription).getCriteria();
|
||||
return ((org.hl7.fhir.r4.model.Subscription) theSubscription).getCriteria();
|
||||
case DSTU2_1:
|
||||
case DSTU2_HL7ORG:
|
||||
default:
|
||||
throw new ConfigurationException("Subscription not supported for version: " + myFhirContext.getVersion().getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setMatchingStrategyTag(FhirContext theFhirContext, IBaseResource theSubscription, SubscriptionMatchingStrategy theStrategy) {
|
||||
public void setMatchingStrategyTag(IBaseResource theSubscription, SubscriptionMatchingStrategy theStrategy) {
|
||||
IBaseMetaType meta = theSubscription.getMeta();
|
||||
String value = theStrategy.toString();
|
||||
String display;
|
||||
|
@ -288,7 +253,7 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
} else if (theStrategy == SubscriptionMatchingStrategy.IN_MEMORY) {
|
||||
display = "In-memory";
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown " + SubscriptionMatchingStrategy.class.getSimpleName() + ": "+theStrategy);
|
||||
throw new IllegalStateException("Unknown " + SubscriptionMatchingStrategy.class.getSimpleName() + ": " + theStrategy);
|
||||
}
|
||||
meta.addTag().setSystem(SubscriptionConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY).setCode(value).setDisplay(display);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package ca.uhn.fhir.jpa.subscription.module;
|
||||
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.contains;
|
||||
|
||||
public class CanonicalSubscriptionTest {
|
||||
|
||||
@Test
|
||||
public void testGetChannelExtension() {
|
||||
|
||||
HashMap<String, List<String>> inMap = new HashMap<>();
|
||||
inMap.put("key1", Lists.newArrayList("VALUE1"));
|
||||
inMap.put("key2", Lists.newArrayList("VALUE2a", "VALUE2b"));
|
||||
|
||||
CanonicalSubscription s = new CanonicalSubscription();
|
||||
s.setChannelExtensions(inMap);
|
||||
|
||||
assertThat(s.getChannelExtension("key1"), Matchers.equalTo("VALUE1"));
|
||||
assertThat(s.getChannelExtension("key2"), Matchers.equalTo("VALUE2a"));
|
||||
assertThat(s.getChannelExtension("key3"), Matchers.nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChannelExtensions() {
|
||||
|
||||
HashMap<String, List<String>> inMap = new HashMap<>();
|
||||
inMap.put("key1", Lists.newArrayList("VALUE1"));
|
||||
inMap.put("key2", Lists.newArrayList("VALUE2a", "VALUE2b"));
|
||||
|
||||
CanonicalSubscription s = new CanonicalSubscription();
|
||||
s.setChannelExtensions(inMap);
|
||||
|
||||
assertThat(s.getChannelExtensions("key1"), Matchers.contains("VALUE1"));
|
||||
assertThat(s.getChannelExtensions("key2"), Matchers.contains("VALUE2a", "VALUE2b"));
|
||||
assertThat(s.getChannelExtensions("key3"), Matchers.empty());
|
||||
}
|
||||
}
|
|
@ -186,7 +186,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
public Class<T> getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
}
|
||||
|
||||
@Read(version = true)
|
||||
public IBaseResource read(@IdParam IIdType theId) {
|
||||
public T read(@IdParam IIdType theId) {
|
||||
TreeMap<Long, T> versions = myIdToVersionToResourceMap.get(theId.getIdPart());
|
||||
if (versions == null || versions.isEmpty()) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
|
@ -240,8 +240,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
}
|
||||
|
||||
@Search
|
||||
public List<IBaseResource> searchAll() {
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
public List<T> searchAll() {
|
||||
List<T> retVal = new ArrayList<>();
|
||||
|
||||
for (TreeMap<Long, T> next : myIdToVersionToResourceMap.values()) {
|
||||
if (next.isEmpty() == false) {
|
||||
|
@ -255,10 +255,10 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
}
|
||||
|
||||
@Search
|
||||
public List<IBaseResource> searchById(
|
||||
public List<T> searchById(
|
||||
@RequiredParam(name = "_id") TokenAndListParam theIds) {
|
||||
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
List<T> retVal = new ArrayList<>();
|
||||
|
||||
for (TreeMap<Long, T> next : myIdToVersionToResourceMap.values()) {
|
||||
if (next.isEmpty() == false) {
|
||||
|
|
Loading…
Reference in New Issue