001package org.hl7.fhir.common.hapi.validation.support; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.context.FhirVersionEnum; 005import ca.uhn.fhir.context.support.IValidationSupport; 006import ca.uhn.fhir.context.support.ValidationSupportContext; 007import ca.uhn.fhir.i18n.Msg; 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 ca.uhn.hapi.converters.canonical.VersionCanonicalizer; 012import org.apache.commons.lang3.Validate; 013import org.hl7.fhir.common.hapi.validation.validator.ProfileKnowledgeWorkerR5; 014import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; 015import org.hl7.fhir.instance.model.api.IBaseResource; 016import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; 017import org.hl7.fhir.r5.conformance.profile.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 java.util.ArrayList; 024 025import static org.apache.commons.lang3.StringUtils.isBlank; 026import static org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService.getFhirVersionEnum; 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 private final VersionCanonicalizer myVersionCanonicalizer; 042 043 /** 044 * Constructor 045 */ 046 public SnapshotGeneratingValidationSupport(FhirContext theCtx) { 047 Validate.notNull(theCtx); 048 myCtx = theCtx; 049 myVersionCanonicalizer = new VersionCanonicalizer(theCtx); 050 } 051 052 @SuppressWarnings("EnhancedSwitchMigration") 053 @Override 054 public IBaseResource generateSnapshot( 055 ValidationSupportContext theValidationSupportContext, 056 IBaseResource theInput, 057 String theUrl, 058 String theWebUrl, 059 String theProfileName) { 060 061 String inputUrl = null; 062 try { 063 FhirVersionEnum version = theInput.getStructureFhirVersionEnum(); 064 065 org.hl7.fhir.r5.model.StructureDefinition inputCanonical = 066 myVersionCanonicalizer.structureDefinitionToCanonical(theInput); 067 068 inputUrl = inputCanonical.getUrl(); 069 if (theValidationSupportContext.getCurrentlyGeneratingSnapshots().contains(inputUrl)) { 070 ourLog.warn("Detected circular dependency, already generating snapshot for: {}", inputUrl); 071 return theInput; 072 } 073 theValidationSupportContext.getCurrentlyGeneratingSnapshots().add(inputUrl); 074 075 String baseDefinition = inputCanonical.getBaseDefinition(); 076 if (isBlank(baseDefinition)) { 077 throw new PreconditionFailedException(Msg.code(704) + "StructureDefinition[id=" 078 + inputCanonical.getIdElement().getId() + ", url=" + inputCanonical.getUrl() + "] has no base"); 079 } 080 081 IBaseResource base = 082 theValidationSupportContext.getRootValidationSupport().fetchStructureDefinition(baseDefinition); 083 if (base == null) { 084 throw new PreconditionFailedException(Msg.code(705) + "Unknown base definition: " + baseDefinition); 085 } 086 087 org.hl7.fhir.r5.model.StructureDefinition baseCanonical = 088 myVersionCanonicalizer.structureDefinitionToCanonical(base); 089 090 if (baseCanonical.getSnapshot().getElement().isEmpty()) { 091 // If the base definition also doesn't have a snapshot, generate that first 092 theValidationSupportContext 093 .getRootValidationSupport() 094 .generateSnapshot(theValidationSupportContext, base, null, null, null); 095 baseCanonical = myVersionCanonicalizer.structureDefinitionToCanonical(base); 096 } 097 098 ArrayList<ValidationMessage> messages = new ArrayList<>(); 099 ProfileKnowledgeProvider profileKnowledgeProvider = new ProfileKnowledgeWorkerR5(myCtx); 100 IWorkerContext context = 101 new VersionSpecificWorkerContextWrapper(theValidationSupportContext, myVersionCanonicalizer); 102 ProfileUtilities profileUtilities = new ProfileUtilities(context, messages, profileKnowledgeProvider); 103 profileUtilities.generateSnapshot(baseCanonical, inputCanonical, theUrl, theWebUrl, theProfileName); 104 105 switch (getFhirVersionEnum( 106 theValidationSupportContext.getRootValidationSupport().getFhirContext(), theInput)) { 107 case DSTU3: 108 org.hl7.fhir.dstu3.model.StructureDefinition generatedDstu3 = 109 (org.hl7.fhir.dstu3.model.StructureDefinition) 110 myVersionCanonicalizer.structureDefinitionFromCanonical(inputCanonical); 111 ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput) 112 .getSnapshot() 113 .getElement() 114 .clear(); 115 ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput) 116 .getSnapshot() 117 .getElement() 118 .addAll(generatedDstu3.getSnapshot().getElement()); 119 break; 120 case R4: 121 org.hl7.fhir.r4.model.StructureDefinition generatedR4 = (org.hl7.fhir.r4.model.StructureDefinition) 122 myVersionCanonicalizer.structureDefinitionFromCanonical(inputCanonical); 123 ((org.hl7.fhir.r4.model.StructureDefinition) theInput) 124 .getSnapshot() 125 .getElement() 126 .clear(); 127 ((org.hl7.fhir.r4.model.StructureDefinition) theInput) 128 .getSnapshot() 129 .getElement() 130 .addAll(generatedR4.getSnapshot().getElement()); 131 break; 132 case R4B: 133 org.hl7.fhir.r4b.model.StructureDefinition generatedR4b = 134 (org.hl7.fhir.r4b.model.StructureDefinition) 135 myVersionCanonicalizer.structureDefinitionFromCanonical(inputCanonical); 136 ((org.hl7.fhir.r4b.model.StructureDefinition) theInput) 137 .getSnapshot() 138 .getElement() 139 .clear(); 140 ((org.hl7.fhir.r4b.model.StructureDefinition) theInput) 141 .getSnapshot() 142 .getElement() 143 .addAll(generatedR4b.getSnapshot().getElement()); 144 break; 145 case R5: 146 org.hl7.fhir.r5.model.StructureDefinition generatedR5 = (org.hl7.fhir.r5.model.StructureDefinition) 147 myVersionCanonicalizer.structureDefinitionFromCanonical(inputCanonical); 148 ((org.hl7.fhir.r5.model.StructureDefinition) theInput) 149 .getSnapshot() 150 .getElement() 151 .clear(); 152 ((org.hl7.fhir.r5.model.StructureDefinition) theInput) 153 .getSnapshot() 154 .getElement() 155 .addAll(generatedR5.getSnapshot().getElement()); 156 break; 157 case DSTU2: 158 case DSTU2_HL7ORG: 159 case DSTU2_1: 160 default: 161 throw new IllegalStateException( 162 Msg.code(706) + "Can not generate snapshot for version: " + version); 163 } 164 165 return theInput; 166 167 } catch (BaseServerResponseException e) { 168 throw e; 169 } catch (Exception e) { 170 throw new InternalErrorException(Msg.code(707) + "Failed to generate snapshot", e); 171 } finally { 172 if (inputUrl != null) { 173 theValidationSupportContext.getCurrentlyGeneratingSnapshots().remove(inputUrl); 174 } 175 } 176 } 177 178 @Override 179 public FhirContext getFhirContext() { 180 return myCtx; 181 } 182 183 @Override 184 public String getName() { 185 return getFhirContext().getVersion().getVersion() + " Snapshot Generating Validation Support"; 186 } 187}