Include profile URL in generated IPS (#5938)
* Include profile URL in generated IPS * Add changelog * Documentation tweak
This commit is contained in:
parent
446869b524
commit
14c364dffd
|
@ -626,6 +626,16 @@ public class BundleBuilder {
|
||||||
terser.setElement(myBundle, "Bundle.timestamp", theTimestamp.getValueAsString());
|
terser.setElement(myBundle, "Bundle.timestamp", theTimestamp.getValueAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a profile URL to <code>Bundle.meta.profile</code>
|
||||||
|
*
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
public void addProfile(String theProfile) {
|
||||||
|
FhirTerser terser = myContext.newTerser();
|
||||||
|
terser.addElement(myBundle, "Bundle.meta.profile", theProfile);
|
||||||
|
}
|
||||||
|
|
||||||
public class DeleteBuilder extends BaseOperationBuilder {
|
public class DeleteBuilder extends BaseOperationBuilder {
|
||||||
|
|
||||||
// nothing yet
|
// nothing yet
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 5938
|
||||||
|
title: "Generated IPS documents will now include a bundle profile declaration."
|
|
@ -42,7 +42,10 @@ public interface IIpsGenerationStrategy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the profile associated with the IPS document
|
* This method returns the profile associated with the IPS document
|
||||||
* generated by this strategy.
|
* generated by this strategy. This URL will be added to generated
|
||||||
|
* IPS Bundles in <code>Bundle.meta.profile</code>, and can also
|
||||||
|
* be used to support the <code>profile</code> parameter on the
|
||||||
|
* <code>$summary</code> operation.
|
||||||
*/
|
*/
|
||||||
String getBundleProfile();
|
String getBundleProfile();
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,7 @@ public class IpsGeneratorSvcImpl implements IIpsGeneratorSvc {
|
||||||
bundleBuilder.setType(Bundle.BundleType.DOCUMENT.toCode());
|
bundleBuilder.setType(Bundle.BundleType.DOCUMENT.toCode());
|
||||||
bundleBuilder.setIdentifier("urn:ietf:rfc:4122", UUID.randomUUID().toString());
|
bundleBuilder.setIdentifier("urn:ietf:rfc:4122", UUID.randomUUID().toString());
|
||||||
bundleBuilder.setTimestamp(InstantType.now());
|
bundleBuilder.setTimestamp(InstantType.now());
|
||||||
|
bundleBuilder.addProfile(theStrategy.getBundleProfile());
|
||||||
|
|
||||||
// Add composition to document
|
// Add composition to document
|
||||||
bundleBuilder.addDocumentEntry(composition);
|
bundleBuilder.addDocumentEntry(composition);
|
||||||
|
|
|
@ -5,7 +5,6 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.ips.api.IIpsGenerationStrategy;
|
import ca.uhn.fhir.jpa.ips.api.IIpsGenerationStrategy;
|
||||||
import ca.uhn.fhir.jpa.ips.jpa.DefaultJpaIpsGenerationStrategy;
|
import ca.uhn.fhir.jpa.ips.jpa.DefaultJpaIpsGenerationStrategy;
|
||||||
import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
|
import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
|
||||||
|
@ -18,6 +17,7 @@ import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||||
import ca.uhn.fhir.validation.ValidationResult;
|
import ca.uhn.fhir.validation.ValidationResult;
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import jakarta.annotation.Nullable;
|
import jakarta.annotation.Nullable;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.NpmPackageValidationSupport;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -31,6 +31,7 @@ import org.hl7.fhir.r4.model.Immunization;
|
||||||
import org.hl7.fhir.r4.model.MedicationStatement;
|
import org.hl7.fhir.r4.model.MedicationStatement;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.PrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.Resource;
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
@ -41,6 +42,7 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -48,10 +50,10 @@ import java.util.stream.Collectors;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||||
|
import static org.hibernate.validator.internal.util.Contracts.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This test uses a complete R4 JPA server as a backend and wires the
|
* This test uses a complete R4 JPA server as a backend and wires the
|
||||||
|
@ -77,7 +79,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateLargePatientSummary() {
|
public void testGenerateLargePatientSummary() throws IOException {
|
||||||
Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/large-patient-everything.json.gz");
|
Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/large-patient-everything.json.gz");
|
||||||
sourceData.setType(Bundle.BundleType.TRANSACTION);
|
sourceData.setType(Bundle.BundleType.TRANSACTION);
|
||||||
for (Bundle.BundleEntryComponent nextEntry : sourceData.getEntry()) {
|
for (Bundle.BundleEntryComponent nextEntry : sourceData.getEntry()) {
|
||||||
|
@ -97,6 +99,9 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
ourLog.info("Output: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
ourLog.info("Output: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
|
assertThat(output.getMeta().getProfile().stream().map(PrimitiveType::getValue).toList(), contains(
|
||||||
|
"http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips"
|
||||||
|
));
|
||||||
validateDocument(output);
|
validateDocument(output);
|
||||||
assertEquals(117, output.getEntry().size());
|
assertEquals(117, output.getEntry().size());
|
||||||
String patientId = findFirstEntryResource(output, Patient.class, 1).getIdElement().toUnqualifiedVersionless().getValue();
|
String patientId = findFirstEntryResource(output, Patient.class, 1).getIdElement().toUnqualifiedVersionless().getValue();
|
||||||
|
@ -162,7 +167,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateTinyPatientSummary() {
|
public void testGenerateTinyPatientSummary() throws IOException {
|
||||||
myStorageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY);
|
myStorageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY);
|
||||||
|
|
||||||
Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/tiny-patient-everything.json.gz");
|
Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/tiny-patient-everything.json.gz");
|
||||||
|
@ -251,7 +256,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static Composition findCompositionSectionByDisplay(Bundle output, String theDisplay) {
|
private static Composition findCompositionSectionByDisplay(Bundle output, @SuppressWarnings("SameParameterValue") String theDisplay) {
|
||||||
Composition composition = (Composition) output.getEntry().get(0).getResource();
|
Composition composition = (Composition) output.getEntry().get(0).getResource();
|
||||||
Composition.SectionComponent section = composition
|
Composition.SectionComponent section = composition
|
||||||
.getSection()
|
.getSection()
|
||||||
|
@ -259,6 +264,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
.filter(t -> t.getCode().getCoding().get(0).getDisplay().equals(theDisplay))
|
.filter(t -> t.getCode().getCoding().get(0).getDisplay().equals(theDisplay))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
|
assertNotNull(section);
|
||||||
return composition;
|
return composition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,23 +272,26 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static List<String> extractSectionTitles(Bundle outcome) {
|
private static List<String> extractSectionTitles(Bundle outcome) {
|
||||||
Composition composition = (Composition) outcome.getEntry().get(0).getResource();
|
Composition composition = (Composition) outcome.getEntry().get(0).getResource();
|
||||||
List<String> sectionTitles = composition
|
return composition
|
||||||
.getSection()
|
.getSection()
|
||||||
.stream()
|
.stream()
|
||||||
.map(Composition.SectionComponent::getTitle)
|
.map(Composition.SectionComponent::getTitle)
|
||||||
.toList();
|
.toList();
|
||||||
return sectionTitles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDocument(Bundle theOutcome) {
|
private void validateDocument(Bundle theOutcome) throws IOException {
|
||||||
FhirValidator validator = myFhirContext.newValidator();
|
FhirValidator validator = myFhirContext.newValidator();
|
||||||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myFhirContext);
|
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myFhirContext);
|
||||||
instanceValidator.setValidationSupport(new ValidationSupportChain(new IpsTerminologySvc(), myFhirContext.getValidationSupport()));
|
|
||||||
|
NpmPackageValidationSupport npmSupport = new NpmPackageValidationSupport(myFhirContext);
|
||||||
|
npmSupport.loadPackageFromClasspath("/ips-package-1.1.0.tgz");
|
||||||
|
|
||||||
|
instanceValidator.setValidationSupport(new ValidationSupportChain(npmSupport, new IpsTerminologySvc(), myFhirContext.getValidationSupport()));
|
||||||
validator.registerValidatorModule(instanceValidator);
|
validator.registerValidatorModule(instanceValidator);
|
||||||
ValidationResult validation = validator.validateWithResult(theOutcome);
|
ValidationResult validation = validator.validateWithResult(theOutcome);
|
||||||
|
|
||||||
Optional<SingleValidationMessage> failure = validation.getMessages().stream().filter(t -> t.getSeverity().ordinal() >= ResultSeverityEnum.ERROR.ordinal()).findFirst();
|
Optional<SingleValidationMessage> failure = validation.getMessages().stream().filter(t -> t.getSeverity().ordinal() >= ResultSeverityEnum.ERROR.ordinal()).findFirst();
|
||||||
assertFalse(failure.isPresent(), () -> failure.get().toString());
|
assertFalse(failure.isPresent(), () -> failure.orElseThrow().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -294,7 +303,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public IIpsGeneratorSvc ipsGeneratorSvc(FhirContext theFhirContext, IIpsGenerationStrategy theGenerationStrategy, DaoRegistry theDaoRegistry) {
|
public IIpsGeneratorSvc ipsGeneratorSvc(FhirContext theFhirContext, IIpsGenerationStrategy theGenerationStrategy) {
|
||||||
return new IpsGeneratorSvcImpl(theFhirContext, theGenerationStrategy);
|
return new IpsGeneratorSvcImpl(theFhirContext, theGenerationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue