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