001/*
002 * #%L
003 * HAPI FHIR JPA Server
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.provider;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.context.RuntimeResourceDefinition;
024import ca.uhn.fhir.context.RuntimeSearchParam;
025import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
026import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
027import ca.uhn.fhir.jpa.util.ResourceCountCache;
028import ca.uhn.fhir.model.api.ExtensionDt;
029import ca.uhn.fhir.model.dstu2.composite.MetaDt;
030import ca.uhn.fhir.model.dstu2.resource.Bundle;
031import ca.uhn.fhir.model.dstu2.resource.Conformance;
032import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest;
033import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource;
034import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam;
035import ca.uhn.fhir.model.dstu2.valueset.ConditionalDeleteStatusEnum;
036import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
037import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
038import ca.uhn.fhir.model.primitive.BoundCodeDt;
039import ca.uhn.fhir.model.primitive.DecimalDt;
040import ca.uhn.fhir.model.primitive.UriDt;
041import ca.uhn.fhir.rest.api.Constants;
042import ca.uhn.fhir.rest.api.server.RequestDetails;
043import ca.uhn.fhir.rest.server.RestfulServer;
044import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
045import ca.uhn.fhir.util.CoverageIgnore;
046import ca.uhn.fhir.util.ExtensionConstants;
047import jakarta.servlet.http.HttpServletRequest;
048import org.hl7.fhir.dstu2.model.Subscription;
049
050import java.util.Collections;
051import java.util.List;
052import java.util.Map;
053
054import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
055import static org.apache.commons.lang3.StringUtils.isNotBlank;
056
057public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
058
059        private volatile Conformance myCachedValue;
060        private JpaStorageSettings myStorageSettings;
061        private String myImplementationDescription;
062        private boolean myIncludeResourceCounts;
063        private RestfulServer myRestfulServer;
064        private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
065        private ResourceCountCache myResourceCountsCache;
066
067        /**
068         * Constructor
069         */
070        @CoverageIgnore
071        public JpaConformanceProviderDstu2() {
072                super();
073                super.setCache(false);
074                setIncludeResourceCounts(true);
075        }
076
077        /**
078         * Constructor
079         */
080        public JpaConformanceProviderDstu2(
081                        RestfulServer theRestfulServer,
082                        IFhirSystemDao<Bundle, MetaDt> theSystemDao,
083                        JpaStorageSettings theStorageSettings) {
084                super(theRestfulServer);
085                myRestfulServer = theRestfulServer;
086                mySystemDao = theSystemDao;
087                myStorageSettings = theStorageSettings;
088                super.setCache(false);
089                setIncludeResourceCounts(true);
090        }
091
092        @Override
093        public Conformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
094                Conformance retVal = myCachedValue;
095
096                Map<String, Long> counts = null;
097                if (myIncludeResourceCounts) {
098                        counts = mySystemDao.getResourceCountsFromCache();
099                }
100                counts = defaultIfNull(counts, Collections.emptyMap());
101
102                FhirContext ctx = myRestfulServer.getFhirContext();
103
104                retVal = super.getServerConformance(theRequest, theRequestDetails);
105                for (Rest nextRest : retVal.getRest()) {
106
107                        for (RestResource nextResource : nextRest.getResource()) {
108
109                                ConditionalDeleteStatusEnum conditionalDelete =
110                                                nextResource.getConditionalDeleteElement().getValueAsEnum();
111                                if (conditionalDelete == ConditionalDeleteStatusEnum.MULTIPLE_DELETES_SUPPORTED
112                                                && myStorageSettings.isAllowMultipleDelete() == false) {
113                                        nextResource.setConditionalDelete(ConditionalDeleteStatusEnum.SINGLE_DELETES_SUPPORTED);
114                                }
115
116                                // Add resource counts
117                                Long count = counts.get(nextResource.getTypeElement().getValueAsString());
118                                if (count != null) {
119                                        nextResource.addUndeclaredExtension(
120                                                        false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count));
121                                }
122
123                                // Add chained params
124                                for (RestResourceSearchParam nextParam : nextResource.getSearchParam()) {
125                                        if (nextParam.getTypeElement().getValueAsEnum() == SearchParamTypeEnum.REFERENCE) {
126                                                List<BoundCodeDt<ResourceTypeEnum>> targets = nextParam.getTarget();
127                                                for (BoundCodeDt<ResourceTypeEnum> next : targets) {
128                                                        RuntimeResourceDefinition def = ctx.getResourceDefinition(next.getValue());
129                                                        for (RuntimeSearchParam nextChainedParam : def.getSearchParams()) {
130                                                                nextParam.addChain(nextChainedParam.getName());
131                                                        }
132                                                }
133                                        }
134                                }
135                        }
136                }
137
138                if (myStorageSettings
139                                .getSupportedSubscriptionTypes()
140                                .contains(Subscription.SubscriptionChannelType.WEBSOCKET)) {
141                        if (isNotBlank(myStorageSettings.getWebsocketContextPath())) {
142                                ExtensionDt websocketExtension = new ExtensionDt();
143                                websocketExtension.setUrl(Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL);
144                                websocketExtension.setValue(new UriDt(myStorageSettings.getWebsocketContextPath()));
145                                retVal.getRestFirstRep().addUndeclaredExtension(websocketExtension);
146                        }
147                }
148
149                if (isNotBlank(myImplementationDescription)) {
150                        retVal.getImplementation().setDescription(myImplementationDescription);
151                }
152                myCachedValue = retVal;
153                return retVal;
154        }
155
156        public boolean isIncludeResourceCounts() {
157                return myIncludeResourceCounts;
158        }
159
160        public void setStorageSettings(JpaStorageSettings theStorageSettings) {
161                this.myStorageSettings = theStorageSettings;
162        }
163
164        @CoverageIgnore
165        public void setImplementationDescription(String theImplDesc) {
166                myImplementationDescription = theImplDesc;
167        }
168
169        public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
170                myIncludeResourceCounts = theIncludeResourceCounts;
171        }
172
173        @Override
174        public void setRestfulServer(RestfulServer theRestfulServer) {
175                this.myRestfulServer = theRestfulServer;
176                super.setRestfulServer(theRestfulServer);
177        }
178
179        @CoverageIgnore
180        public void setSystemDao(IFhirSystemDao<Bundle, MetaDt> mySystemDao) {
181                this.mySystemDao = mySystemDao;
182        }
183}