001package org.hl7.fhir.common.hapi.validation.support; 002 003import ca.uhn.fhir.i18n.Msg; 004import ca.uhn.fhir.context.FhirContext; 005import ca.uhn.fhir.context.FhirVersionEnum; 006import ca.uhn.fhir.context.support.IValidationSupport; 007import ca.uhn.fhir.context.support.ValidationSupportContext; 008import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; 009import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 010import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; 011import org.apache.commons.lang3.Validate; 012import org.hl7.fhir.common.hapi.validation.validator.ProfileKnowledgeWorkerR5; 013import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; 014import org.hl7.fhir.common.hapi.validation.validator.VersionTypeConverterDstu3; 015import org.hl7.fhir.common.hapi.validation.validator.VersionTypeConverterR4; 016import org.hl7.fhir.instance.model.api.IBaseResource; 017import org.hl7.fhir.r5.conformance.ProfileUtilities; 018import org.hl7.fhir.r5.context.IWorkerContext; 019import org.hl7.fhir.utilities.validation.ValidationMessage; 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023import javax.annotation.Nullable; 024import java.util.ArrayList; 025 026import static org.apache.commons.lang3.StringUtils.isBlank; 027 028/** 029 * Simple validation support module that handles profile snapshot generation. 030 * <p> 031 * This module currently supports the following FHIR versions: 032 * <ul> 033 * <li>DSTU3</li> 034 * <li>R4</li> 035 * <li>R5</li> 036 * </ul> 037 */ 038public class SnapshotGeneratingValidationSupport implements IValidationSupport { 039 private static final Logger ourLog = LoggerFactory.getLogger(SnapshotGeneratingValidationSupport.class); 040 private final FhirContext myCtx; 041 042 /** 043 * Constructor 044 */ 045 public SnapshotGeneratingValidationSupport(FhirContext theCtx) { 046 Validate.notNull(theCtx); 047 myCtx = theCtx; 048 } 049 050 @Override 051 public IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) { 052 053 String inputUrl = null; 054 try { 055 FhirVersionEnum version = theInput.getStructureFhirVersionEnum(); 056 assert version == myCtx.getVersion().getVersion(); 057 058 VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter = newVersionTypeConverter(version); 059 Validate.notNull(converter, "Can not generate snapshot for version: %s", version); 060 061 org.hl7.fhir.r5.model.StructureDefinition inputCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(theInput); 062 063 inputUrl = inputCanonical.getUrl(); 064 if (theValidationSupportContext.getCurrentlyGeneratingSnapshots().contains(inputUrl)) { 065 ourLog.warn("Detected circular dependency, already generating snapshot for: {}", inputUrl); 066 return theInput; 067 } 068 theValidationSupportContext.getCurrentlyGeneratingSnapshots().add(inputUrl); 069 070 String baseDefinition = inputCanonical.getBaseDefinition(); 071 if (isBlank(baseDefinition)) { 072 throw new PreconditionFailedException(Msg.code(704) + "StructureDefinition[id=" + inputCanonical.getIdElement().getId() + ", url=" + inputCanonical.getUrl() + "] has no base"); 073 } 074 075 IBaseResource base = theValidationSupportContext.getRootValidationSupport().fetchStructureDefinition(baseDefinition); 076 if (base == null) { 077 throw new PreconditionFailedException(Msg.code(705) + "Unknown base definition: " + baseDefinition); 078 } 079 080 org.hl7.fhir.r5.model.StructureDefinition baseCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(base); 081 082 if (baseCanonical.getSnapshot().getElement().isEmpty()) { 083 // If the base definition also doesn't have a snapshot, generate that first 084 theValidationSupportContext.getRootValidationSupport().generateSnapshot(theValidationSupportContext, base, null, null, null); 085 baseCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(base); 086 } 087 088 ArrayList<ValidationMessage> messages = new ArrayList<>(); 089 org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider profileKnowledgeProvider = new ProfileKnowledgeWorkerR5(myCtx); 090 IWorkerContext context = new VersionSpecificWorkerContextWrapper(theValidationSupportContext, converter); 091 ProfileUtilities profileUtilities = new ProfileUtilities(context, messages, profileKnowledgeProvider); 092 profileUtilities.generateSnapshot(baseCanonical, inputCanonical, theUrl, theWebUrl, theProfileName); 093 094 switch (version) { 095 case DSTU3: 096 org.hl7.fhir.dstu3.model.StructureDefinition generatedDstu3 = (org.hl7.fhir.dstu3.model.StructureDefinition) converter.fromCanonical(inputCanonical); 097 ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); 098 ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput).getSnapshot().getElement().addAll(generatedDstu3.getSnapshot().getElement()); 099 break; 100 case R4: 101 org.hl7.fhir.r4.model.StructureDefinition generatedR4 = (org.hl7.fhir.r4.model.StructureDefinition) converter.fromCanonical(inputCanonical); 102 ((org.hl7.fhir.r4.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); 103 ((org.hl7.fhir.r4.model.StructureDefinition) theInput).getSnapshot().getElement().addAll(generatedR4.getSnapshot().getElement()); 104 break; 105 case R5: 106 org.hl7.fhir.r5.model.StructureDefinition generatedR5 = (org.hl7.fhir.r5.model.StructureDefinition) converter.fromCanonical(inputCanonical); 107 ((org.hl7.fhir.r5.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); 108 ((org.hl7.fhir.r5.model.StructureDefinition) theInput).getSnapshot().getElement().addAll(generatedR5.getSnapshot().getElement()); 109 break; 110 case DSTU2: 111 case DSTU2_HL7ORG: 112 case DSTU2_1: 113 default: 114 throw new IllegalStateException(Msg.code(706) + "Can not generate snapshot for version: " + version); 115 } 116 117 return theInput; 118 119 } catch (BaseServerResponseException e) { 120 throw e; 121 } catch (Exception e) { 122 throw new InternalErrorException(Msg.code(707) + "Failed to generate snapshot", e); 123 } finally { 124 if (inputUrl != null) { 125 theValidationSupportContext.getCurrentlyGeneratingSnapshots().remove(inputUrl); 126 } 127 } 128 } 129 130 @Override 131 public FhirContext getFhirContext() { 132 return myCtx; 133 } 134 135 @Nullable 136 public static VersionSpecificWorkerContextWrapper.IVersionTypeConverter newVersionTypeConverter(FhirVersionEnum version) { 137 VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter; 138 switch (version) { 139 case DSTU3: 140 converter = new VersionTypeConverterDstu3(); 141 break; 142 case R4: 143 converter = new VersionTypeConverterR4(); 144 break; 145 case R5: 146 converter = VersionSpecificWorkerContextWrapper.IDENTITY_VERSION_TYPE_CONVERTER; 147 break; 148 case DSTU2: 149 case DSTU2_HL7ORG: 150 case DSTU2_1: 151 default: 152 return null; 153 } 154 return converter; 155 } 156 157 158}