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}