
001/*- 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.validation; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.context.RuntimeResourceDefinition; 024import ca.uhn.fhir.context.support.IValidationSupport; 025import ca.uhn.fhir.i18n.Msg; 026import ca.uhn.fhir.jpa.api.dao.DaoRegistry; 027import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; 028import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 029import ca.uhn.fhir.rest.api.server.RequestDetails; 030import ca.uhn.fhir.rest.param.TokenParam; 031import ca.uhn.fhir.rest.param.UriParam; 032import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 033import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 034import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; 035import org.hl7.fhir.exceptions.FHIRException; 036import org.hl7.fhir.instance.model.api.IBaseResource; 037import org.hl7.fhir.r4.model.IdType; 038import org.hl7.fhir.r5.elementmodel.Element; 039import org.hl7.fhir.r5.elementmodel.JsonParser; 040import org.hl7.fhir.r5.model.CanonicalResource; 041import org.hl7.fhir.r5.model.CanonicalType; 042import org.hl7.fhir.r5.utils.validation.IResourceValidator; 043import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; 044import org.hl7.fhir.utilities.CanonicalPair; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048import java.util.Collections; 049import java.util.List; 050import java.util.Locale; 051import java.util.Set; 052 053/** 054 * Please note that this bean is not currently used as part of the $validate operation. 055 * The FHIR Core validation library uses {@link VersionSpecificWorkerContextWrapper} to retrieve validation resources. 056 */ 057public class ValidatorResourceFetcher implements IValidatorResourceFetcher { 058 059 private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class); 060 061 private final FhirContext myFhirContext; 062 private final DaoRegistry myDaoRegistry; 063 private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper; 064 065 public ValidatorResourceFetcher( 066 FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) { 067 myFhirContext = theFhirContext; 068 myDaoRegistry = theDaoRegistry; 069 myVersionSpecificContextWrapper = 070 VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(theValidationSupport); 071 } 072 073 @Override 074 public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) throws FHIRException { 075 IdType id = new IdType(theUrl); 076 String resourceType = id.getResourceType(); 077 IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceType); 078 IBaseResource target; 079 try { 080 target = dao.read(id, (RequestDetails) appContext); 081 } catch (ResourceNotFoundException e) { 082 ourLog.info("Failed to resolve local reference: {}", theUrl); 083 084 RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(resourceType); 085 if (def.getChildByName("url") != null) { 086 try { 087 target = fetchByUrl(theUrl, dao, (RequestDetails) appContext); 088 } catch (ResourceNotFoundException e2) { 089 ourLog.info("Failed to find resource by URL: {}", theUrl); 090 return null; 091 } 092 } else { 093 return null; 094 } 095 } 096 try { 097 return new JsonParser(myVersionSpecificContextWrapper) 098 .parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType); 099 } catch (Exception e) { 100 throw new FHIRException(Msg.code(576) + e, e); 101 } 102 } 103 104 private IBaseResource fetchByUrl(String url, IFhirResourceDao<?> dao, RequestDetails requestDetails) 105 throws ResourceNotFoundException { 106 CanonicalPair pair = new CanonicalPair(url); 107 SearchParameterMap searchParameterMap = new SearchParameterMap(); 108 searchParameterMap.add("url", new UriParam(pair.getUrl())); 109 String version = pair.getVersion(); 110 if (version != null && !version.isEmpty()) { 111 searchParameterMap.add("version", new TokenParam(version)); 112 } 113 List<IBaseResource> results = null; 114 try { 115 results = dao.search(searchParameterMap, requestDetails).getAllResources(); 116 } catch (InvalidRequestException e) { 117 ourLog.info("Resource does not support 'url' or 'version' Search Parameters"); 118 } 119 if (results != null && !results.isEmpty()) { 120 if (results.size() > 1) { 121 ourLog.warn("Multiple results found for URL '{}', only the first will be considered.", url); 122 } 123 return results.get(0); 124 } else { 125 throw new ResourceNotFoundException(Msg.code(2444) + "Failed to find resource by URL: " + url); 126 } 127 } 128 129 @Override 130 public boolean resolveURL( 131 IResourceValidator iResourceValidator, 132 Object o, 133 String s, 134 String s1, 135 String s2, 136 boolean isCanonical, 137 List<CanonicalType> targets) { 138 return true; 139 } 140 141 @Override 142 public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws UnsupportedOperationException { 143 throw new UnsupportedOperationException(Msg.code(577)); 144 } 145 146 @Override 147 public IValidatorResourceFetcher setLocale(Locale locale) { 148 // ignore 149 return this; 150 } 151 152 @Override 153 public CanonicalResource fetchCanonicalResource(IResourceValidator validator, Object appContext, String url) { 154 return null; 155 } 156 157 @Override 158 public boolean fetchesCanonicalResource(IResourceValidator iResourceValidator, String s) { 159 return false; 160 } 161 162 @Override 163 public Set<String> fetchCanonicalResourceVersions(IResourceValidator validator, Object appContext, String url) { 164 return Collections.emptySet(); 165 } 166}