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.utils.validation.IResourceValidator; 042import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; 043import org.hl7.fhir.utilities.CanonicalPair; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047import java.util.Collections; 048import java.util.List; 049import java.util.Locale; 050import java.util.Set; 051 052/** 053 * Please note that this bean is not currently used as part of the $validate operation. 054 * The FHIR Core validation library uses {@link VersionSpecificWorkerContextWrapper} to retrieve validation resources. 055 */ 056public class ValidatorResourceFetcher implements IValidatorResourceFetcher { 057 058 private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class); 059 060 private final FhirContext myFhirContext; 061 private final DaoRegistry myDaoRegistry; 062 private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper; 063 064 public ValidatorResourceFetcher( 065 FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) { 066 myFhirContext = theFhirContext; 067 myDaoRegistry = theDaoRegistry; 068 myVersionSpecificContextWrapper = 069 VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(theValidationSupport); 070 } 071 072 @Override 073 public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) throws FHIRException { 074 IdType id = new IdType(theUrl); 075 String resourceType = id.getResourceType(); 076 IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceType); 077 IBaseResource target; 078 try { 079 target = dao.read(id, (RequestDetails) appContext); 080 } catch (ResourceNotFoundException e) { 081 ourLog.info("Failed to resolve local reference: {}", theUrl); 082 083 RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(resourceType); 084 if (def.getChildByName("url") != null) { 085 try { 086 target = fetchByUrl(theUrl, dao, (RequestDetails) appContext); 087 } catch (ResourceNotFoundException e2) { 088 ourLog.info("Failed to find resource by URL: {}", theUrl); 089 return null; 090 } 091 } else { 092 return null; 093 } 094 } 095 try { 096 return new JsonParser(myVersionSpecificContextWrapper) 097 .parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType); 098 } catch (Exception e) { 099 throw new FHIRException(Msg.code(576) + e, e); 100 } 101 } 102 103 private IBaseResource fetchByUrl(String url, IFhirResourceDao<?> dao, RequestDetails requestDetails) 104 throws ResourceNotFoundException { 105 CanonicalPair pair = new CanonicalPair(url); 106 SearchParameterMap searchParameterMap = new SearchParameterMap(); 107 searchParameterMap.add("url", new UriParam(pair.getUrl())); 108 String version = pair.getVersion(); 109 if (version != null && !version.isEmpty()) { 110 searchParameterMap.add("version", new TokenParam(version)); 111 } 112 List<IBaseResource> results = null; 113 try { 114 results = dao.search(searchParameterMap, requestDetails).getAllResources(); 115 } catch (InvalidRequestException e) { 116 ourLog.info("Resource does not support 'url' or 'version' Search Parameters"); 117 } 118 if (results != null && !results.isEmpty()) { 119 if (results.size() > 1) { 120 ourLog.warn("Multiple results found for URL '{}', only the first will be considered.", url); 121 } 122 return results.get(0); 123 } else { 124 throw new ResourceNotFoundException(Msg.code(2444) + "Failed to find resource by URL: " + url); 125 } 126 } 127 128 @Override 129 public boolean resolveURL( 130 IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) { 131 return true; 132 } 133 134 @Override 135 public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws UnsupportedOperationException { 136 throw new UnsupportedOperationException(Msg.code(577)); 137 } 138 139 @Override 140 public IValidatorResourceFetcher setLocale(Locale locale) { 141 // ignore 142 return this; 143 } 144 145 @Override 146 public CanonicalResource fetchCanonicalResource(IResourceValidator validator, Object appContext, String url) { 147 return null; 148 } 149 150 @Override 151 public boolean fetchesCanonicalResource(IResourceValidator iResourceValidator, String s) { 152 return false; 153 } 154 155 @Override 156 public Set<String> fetchCanonicalResourceVersions(IResourceValidator validator, Object appContext, String url) { 157 return Collections.emptySet(); 158 } 159}