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