improve validation for R4 backport subscriptions in SubscriptionValidatingInterceptor (#5973)

* still need to genericise the rest of the SubscriptionValidatingInterceptorTest tests
also there are Msg.code FIXMEs

* cleanup
still need to genericise the rest of the SubscriptionValidatingInterceptorTest tests

* cleanup
still need to genericise the rest of the SubscriptionValidatingInterceptorTest tests

* done

* done

* javadoc

* changelog

* fix test

* fix test

* fix test

* fix test

* fix test
This commit is contained in:
Ken Stevens 2024-05-29 14:58:14 -04:00 committed by GitHub
parent 4ac65da2ee
commit ac3a5e2ad2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 296 additions and 70 deletions

View File

@ -464,6 +464,21 @@ public final class TerserUtil {
mergeFields(theTerser, theResource, childDefinition, theFromFieldValues, theToFieldValues);
}
/**
* Sets the provided field with the given values. This method will add to the collection of existing field values
* in case of multiple cardinality. Use {@link #clearField(FhirContext, IBaseResource, String)}
* to remove values before setting
*
* @param theFhirContext Context holding resource definition
* @param theFieldName Child field name of the resource to set
* @param theResource The resource to set the values on
* @param theValue The String value to set on the resource child field name. This value is converted to the appropriate primitive type before the value is set
*/
public static void setStringField(
FhirContext theFhirContext, String theFieldName, IBaseResource theResource, String theValue) {
setField(theFhirContext, theFieldName, theResource, theFhirContext.newPrimitiveString(theValue));
}
/**
* Sets the specified value at the FHIR path provided.
*
@ -493,6 +508,20 @@ public final class TerserUtil {
setFieldByFhirPath(theFhirContext.newTerser(), theFhirPath, theResource, theValue);
}
/**
* Sets the specified String value at the FHIR path provided.
*
* @param theFhirContext Context holding resource definition
* @param theFhirPath The FHIR path to set the field at
* @param theResource The resource on which the value should be set
* @param theValue The String value to set. The string is converted to the appropriate primitive type before setting the field
*/
public static void setStringFieldByFhirPath(
FhirContext theFhirContext, String theFhirPath, IBaseResource theResource, String theValue) {
setFieldByFhirPath(
theFhirContext.newTerser(), theFhirPath, theResource, theFhirContext.newPrimitiveString(theValue));
}
/**
* Returns field values ant the specified FHIR path from the resource.
*

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5973
title: "Previously, subscription criteria on R4 backport subscriptions were not validated by the
SubscriptionValidatingInterceptor. This has been corrected."

View File

@ -46,15 +46,21 @@ import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.subscription.SubscriptionConstants;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.SubscriptionUtil;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Subscription;
import org.hl7.fhir.r5.model.SubscriptionTopic;
import org.springframework.beans.factory.annotation.Autowired;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -62,9 +68,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
@Interceptor
public class SubscriptionValidatingInterceptor {
@Autowired
private SubscriptionCanonicalizer mySubscriptionCanonicalizer;
@Autowired
private DaoRegistry myDaoRegistry;
@ -74,6 +77,9 @@ public class SubscriptionValidatingInterceptor {
@Autowired
private SubscriptionStrategyEvaluator mySubscriptionStrategyEvaluator;
@Autowired
private SubscriptionCanonicalizer mySubscriptionCanonicalizer;
private FhirContext myFhirContext;
@Autowired
@ -150,27 +156,13 @@ public class SubscriptionValidatingInterceptor {
if (!finished) {
if (subscription.isTopicSubscription()) {
if (myFhirContext.getVersion().getVersion()
!= FhirVersionEnum
.R4) { // In R4 topic subscriptions exist without a corresponidng SubscriptionTopic
// resource
Optional<IBaseResource> oTopic = findSubscriptionTopicByUrl(subscription.getTopic());
if (!oTopic.isPresent()) {
throw new UnprocessableEntityException(
Msg.code(2322) + "No SubscriptionTopic exists with topic: " + subscription.getTopic());
}
}
} else {
validateQuery(subscription.getCriteriaString(), "Subscription.criteria");
if (subscription.getPayloadSearchCriteria() != null) {
validateQuery(
subscription.getPayloadSearchCriteria(),
"Subscription.extension(url='" + HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA
+ "')");
}
if (subscription.getPayloadSearchCriteria() != null) {
validateQuery(
subscription.getPayloadSearchCriteria(),
"Subscription.extension(url='" + HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA
+ "')");
}
validateCriteria(theSubscription, subscription);
validateChannelType(subscription);
@ -197,6 +189,52 @@ public class SubscriptionValidatingInterceptor {
}
}
private void validateCriteria(IBaseResource theSubscription, CanonicalSubscription theCanonicalSubscription) {
if (theCanonicalSubscription.isTopicSubscription()) {
if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.R4) {
validateR4BackportSubscription((Subscription) theSubscription);
} else {
validateR5PlusTopicSubscription(theCanonicalSubscription);
}
} else {
validateQuery(theCanonicalSubscription.getCriteriaString(), "Subscription.criteria");
}
}
private void validateR5PlusTopicSubscription(CanonicalSubscription theCanonicalSubscription) {
Optional<IBaseResource> oTopic = findSubscriptionTopicByUrl(theCanonicalSubscription.getTopic());
if (!oTopic.isPresent()) {
throw new UnprocessableEntityException(
Msg.code(2322) + "No SubscriptionTopic exists with topic: " + theCanonicalSubscription.getTopic());
}
}
private void validateR4BackportSubscription(Subscription theSubscription) {
// This is an R4 backport topic subscription
// In R4, topic subscriptions exist without a corresponding SubscriptionTopic
Subscription r4Subscription = theSubscription;
List<String> filterUrls = new ArrayList<>();
List<Extension> filterUrlExtensions = r4Subscription
.getCriteriaElement()
.getExtensionsByUrl(SubscriptionConstants.SUBSCRIPTION_TOPIC_FILTER_URL);
filterUrlExtensions.forEach(filterUrlExtension -> {
StringType filterUrlElement = (StringType) filterUrlExtension.getValue();
if (filterUrlElement != null) {
filterUrls.add(filterUrlElement.getValue());
}
});
if (filterUrls.isEmpty()) {
// Trigger a "no criteria" validation exception
validateQuery(
null,
"Subscription.criteria.extension with url " + SubscriptionConstants.SUBSCRIPTION_TOPIC_FILTER_URL);
} else {
filterUrls.forEach(filterUrl -> validateQuery(
filterUrl,
"Subscription.criteria.extension with url " + SubscriptionConstants.SUBSCRIPTION_TOPIC_FILTER_URL));
}
}
protected void validatePermissions(
IBaseResource theSubscription,
CanonicalSubscription theCanonicalSubscription,

View File

@ -0,0 +1,83 @@
package ca.uhn.fhir.jpa.subscription.submit.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
import ca.uhn.fhir.subscription.SubscriptionConstants;
import ca.uhn.fhir.util.TerserUtil;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Version independent utility class for setting fields on subscriptions
*/
public final class SubscriptionUtil {
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionUtil.class);
private SubscriptionUtil() {}
public static void setStatus(FhirContext theFhirContext, IBaseResource theSubscription, String theStatus) {
IBaseEnumeration newValue = switch (theFhirContext.getVersion().getVersion()) {
case DSTU3 -> new org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatusEnumFactory().fromType(new org.hl7.fhir.dstu3.model.StringType(theStatus));
case R4 -> new org.hl7.fhir.r4.model.Subscription.SubscriptionStatusEnumFactory().fromType(new org.hl7.fhir.r4.model.StringType(theStatus));
case R4B -> new org.hl7.fhir.r4b.model.Enumerations.SubscriptionStatusEnumFactory().fromType(new org.hl7.fhir.r4b.model.StringType(theStatus));
case R5 -> new org.hl7.fhir.r5.model.Enumerations.SubscriptionStatusCodesEnumFactory().fromType(new org.hl7.fhir.r5.model.StringType(theStatus));
default -> null;
};
TerserUtil.setField(theFhirContext, "status", theSubscription, newValue);
}
public static void setCriteria(FhirContext theFhirContext, IBaseResource theSubscription, String theCriteria) {
SubscriptionCanonicalizer canonicalizer = new SubscriptionCanonicalizer(theFhirContext);
CanonicalSubscription canonicalSubscription = canonicalizer.canonicalize(theSubscription);
if (canonicalSubscription.isTopicSubscription()) {
if (theFhirContext.getVersion().getVersion() == FhirVersionEnum.R5) {
// Nothing to do on R5
return;
}
Subscription subscription = (Subscription)theSubscription;
subscription.getCriteriaElement().addExtension(SubscriptionConstants.SUBSCRIPTION_TOPIC_FILTER_URL, new StringType(theCriteria));
} else {
TerserUtil.setStringField(theFhirContext, "criteria", theSubscription, theCriteria);
}
}
public static void setChannelType(FhirContext theFhirContext, IBaseResource theSubscription, String theChannelType) {
FhirVersionEnum version = theFhirContext.getVersion().getVersion();
IBase newValue = switch (version) {
case DSTU3 -> new org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelTypeEnumFactory().fromType(new org.hl7.fhir.dstu3.model.StringType(theChannelType));
case R4 -> new org.hl7.fhir.r4.model.Subscription.SubscriptionChannelTypeEnumFactory().fromType(new org.hl7.fhir.r4.model.StringType(theChannelType));
case R4B -> new org.hl7.fhir.r4b.model.Subscription.SubscriptionChannelTypeEnumFactory().fromType(new org.hl7.fhir.r4b.model.StringType(theChannelType));
case R5 -> CanonicalSubscriptionChannelType.valueOf(theChannelType.toUpperCase()).toR5Coding();
default -> null;
};
String fhirPath = switch(version) {
case DSTU3 -> "channel.type";
case R4 -> "channel.type";
case R4B -> "channel.type";
case R5 -> "channelType";
default -> null;
};
TerserUtil.setFieldByFhirPath(theFhirContext, fhirPath, theSubscription, newValue);
}
public static void setEndpoint(FhirContext theFhirContext, IBaseResource theSubscription, String theEndpoint) {
FhirVersionEnum version = theFhirContext.getVersion().getVersion();
String fhirPath = switch(version) {
case DSTU3 -> "channel.endpoint";
case R4 -> "channel.endpoint";
case R4B -> "channel.endpoint";
case R5 -> "endpoint";
default -> null;
};
TerserUtil.setStringFieldByFhirPath(theFhirContext, fhirPath, theSubscription, theEndpoint);
}
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.subscription.submit.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
@ -8,11 +9,16 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.subscription.SubscriptionConstants;
import jakarta.annotation.Nonnull;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4b.model.CanonicalType;
import org.hl7.fhir.r4b.model.Enumerations;
import org.hl7.fhir.r4b.model.Subscription;
@ -20,6 +26,8 @@ import org.hl7.fhir.r4b.model.SubscriptionTopic;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -29,14 +37,18 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import jakarta.annotation.Nonnull;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import static ca.uhn.fhir.subscription.SubscriptionTestDataHelper.TEST_TOPIC;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ -58,17 +70,20 @@ public class SubscriptionValidatingInterceptorTest {
private IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
@Mock
private IFhirResourceDao<SubscriptionTopic> mySubscriptionTopicDao;
private FhirContext myFhirContext;
@BeforeEach
public void before() {
setFhirContext(FhirVersionEnum.R4B);
when(myDaoRegistry.isResourceTypeSupported(any())).thenReturn(true);
}
@Test
public void testEmptySub() {
@ParameterizedTest
@MethodSource("subscriptionByFhirVersion345")
public void testEmptySub(IBaseResource theSubscription) {
try {
Subscription badSub = new Subscription();
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
setFhirContext(theSubscription);
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(8) + "Can not process submitted Subscription - Subscription.status must be populated on this server"));
@ -76,94 +91,93 @@ public class SubscriptionValidatingInterceptorTest {
}
}
@Test
public void testEmptyStatus() {
@ParameterizedTest
@MethodSource("subscriptionByFhirVersion34") // R5 subscriptions don't have criteria
public void testEmptyCriteria(IBaseResource theSubscription) {
try {
Subscription badSub = new Subscription();
badSub.setStatus(Enumerations.SubscriptionStatus.ACTIVE);
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
initSubscription(theSubscription);
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(11) + "Subscription.criteria must be populated"));
assertThat(e.getMessage(), startsWith(Msg.code(11) + "Subscription."));
assertThat(e.getMessage(), endsWith( " must be populated"));
}
}
@Test
public void testBadCriteria() {
@ParameterizedTest
@MethodSource("subscriptionByFhirVersion34")
public void testBadCriteria(IBaseResource theSubscription) {
try {
Subscription badSub = new Subscription();
badSub.setStatus(Enumerations.SubscriptionStatus.ACTIVE);
badSub.setCriteria("Patient");
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
initSubscription(theSubscription);
SubscriptionUtil.setCriteria(myFhirContext, theSubscription, "Patient");
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(14) + "Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
assertThat(e.getMessage(), endsWith("criteria must be in the form \"{Resource Type}?[params]\""));
}
}
@Test
public void testBadChannel() {
@ParameterizedTest
@MethodSource("subscriptionByFhirVersion34")
public void testBadChannel(IBaseResource theSubscription) {
try {
Subscription badSub = new Subscription();
badSub.setStatus(Enumerations.SubscriptionStatus.ACTIVE);
badSub.setCriteria("Patient?");
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
initSubscription(theSubscription);
SubscriptionUtil.setCriteria(myFhirContext, theSubscription, "Patient?");
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(20) + "Subscription.channel.type must be populated"));
}
}
@Test
public void testEmptyEndpoint() {
@ParameterizedTest
@MethodSource("subscriptionByFhirVersion345")
public void testEmptyEndpoint(IBaseResource theSubscription) {
try {
Subscription badSub = new Subscription();
badSub.setStatus(Enumerations.SubscriptionStatus.ACTIVE);
badSub.setCriteria("Patient?");
Subscription.SubscriptionChannelComponent channel = badSub.getChannel();
channel.setType(Subscription.SubscriptionChannelType.MESSAGE);
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
initSubscription(theSubscription);
SubscriptionUtil.setCriteria(myFhirContext, theSubscription, "Patient?");
SubscriptionUtil.setChannelType(myFhirContext, theSubscription, "message");
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(16) + "No endpoint defined for message subscription"));
}
}
@Test
public void testMalformedEndpoint() {
Subscription badSub = new Subscription();
badSub.setStatus(Enumerations.SubscriptionStatus.ACTIVE);
badSub.setCriteria("Patient?");
Subscription.SubscriptionChannelComponent channel = badSub.getChannel();
channel.setType(Subscription.SubscriptionChannelType.MESSAGE);
@ParameterizedTest
@MethodSource("subscriptionByFhirVersion345")
public void testMalformedEndpoint(IBaseResource theSubscription) {
initSubscription(theSubscription);
SubscriptionUtil.setCriteria(myFhirContext, theSubscription, "Patient?");
SubscriptionUtil.setChannelType(myFhirContext, theSubscription, "message");
SubscriptionUtil.setEndpoint(myFhirContext, theSubscription, "foo");
channel.setEndpoint("foo");
try {
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(17) + "Only 'channel' protocol is supported for Subscriptions with channel type 'message'"));
}
channel.setEndpoint("channel");
SubscriptionUtil.setEndpoint(myFhirContext, theSubscription, "channel");
try {
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(17) + "Only 'channel' protocol is supported for Subscriptions with channel type 'message'"));
}
channel.setEndpoint("channel:");
SubscriptionUtil.setEndpoint(myFhirContext, theSubscription, "channel:");
try {
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), is(Msg.code(19) + "Invalid subscription endpoint uri channel:"));
}
// Happy path
channel.setEndpoint("channel:my-queue-name");
mySubscriptionValidatingInterceptor.resourcePreCreate(badSub, null, null);
SubscriptionUtil.setEndpoint(myFhirContext, theSubscription, "channel:my-queue-name");
mySubscriptionValidatingInterceptor.resourcePreCreate(theSubscription, null, null);
}
@Test
@ -213,6 +227,58 @@ public class SubscriptionValidatingInterceptorTest {
mySubscriptionValidatingInterceptor.validateSubmittedSubscription(badSub, null, null, Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED);
}
private void initSubscription(IBaseResource theSubscription) {
setFhirContext(theSubscription);
SubscriptionUtil.setStatus(myFhirContext, theSubscription, "active");
if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.R5) {
initR5();
org.hl7.fhir.r5.model.Subscription subscription = (org.hl7.fhir.r5.model.Subscription) theSubscription;
subscription.setTopic(TEST_TOPIC);
}
}
void initR5() {
when(myDaoRegistry.getResourceDao("SubscriptionTopic")).thenReturn(mySubscriptionTopicDao);
org.hl7.fhir.r5.model.SubscriptionTopic topic = new org.hl7.fhir.r5.model.SubscriptionTopic();
IBundleProvider provider = new SimpleBundleProvider(topic);
when(mySubscriptionTopicDao.search(any(SearchParameterMap.class), any(RequestDetails.class))).thenReturn(provider);
}
public static Stream<IBaseResource> subscriptionByFhirVersion345() {
return subscriptionByFhirVersion(true);
}
public static Stream<IBaseResource> subscriptionByFhirVersion34() {
return subscriptionByFhirVersion(false);
}
private void setFhirContext(IBaseResource theSubscription) {
FhirVersionEnum fhirVersion = theSubscription.getStructureFhirVersionEnum();
setFhirContext(fhirVersion);
}
private void setFhirContext(FhirVersionEnum fhirVersion) {
myFhirContext = FhirContext.forCached(fhirVersion);
mySubscriptionValidatingInterceptor.setFhirContext(myFhirContext);
mySubscriptionValidatingInterceptor.setSubscriptionCanonicalizerForUnitTest(new SubscriptionCanonicalizer(myFhirContext, myStorageSettings));
}
private static @Nonnull Stream<IBaseResource> subscriptionByFhirVersion(boolean theIncludeR5) {
List<IBaseResource> resources = new ArrayList<>();
resources.add(new org.hl7.fhir.dstu3.model.Subscription());
resources.add(new org.hl7.fhir.r4.model.Subscription());
org.hl7.fhir.r4.model.Subscription r4Backport = new org.hl7.fhir.r4.model.Subscription();
r4Backport.getMeta().addProfile(SubscriptionConstants.SUBSCRIPTION_TOPIC_PROFILE_URL);
resources.add(r4Backport);
resources.add(new org.hl7.fhir.r4b.model.Subscription());
if (theIncludeR5) {
resources.add(new org.hl7.fhir.r5.model.Subscription());
}
return resources.stream();
}
@Configuration
public static class SpringConfig {
@Bean

View File

@ -24,6 +24,7 @@ import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Coding;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -158,4 +159,8 @@ public enum CanonicalSubscriptionChannelType {
public Subscription.SubscriptionChannelType toCanonical() {
return Subscription.SubscriptionChannelType.fromCode(toCode());
}
public Coding toR5Coding() {
return new Coding().setSystem(getSystem()).setCode(toCode());
}
}