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