001/*
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2023 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 org.hl7.fhir.dstu2.model.Subscription;
048
049import javax.servlet.http.HttpServletRequest;
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(RestfulServer theRestfulServer, IFhirSystemDao<Bundle, MetaDt> theSystemDao, JpaStorageSettings theStorageSettings) {
081                super(theRestfulServer);
082                myRestfulServer = theRestfulServer;
083                mySystemDao = theSystemDao;
084                myStorageSettings = theStorageSettings;
085                super.setCache(false);
086                setIncludeResourceCounts(true);
087        }
088
089        @Override
090        public Conformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
091                Conformance retVal = myCachedValue;
092
093                Map<String, Long> counts = null;
094                if (myIncludeResourceCounts) {
095                        counts = mySystemDao.getResourceCountsFromCache();
096                }
097                counts = defaultIfNull(counts, Collections.emptyMap());
098
099                FhirContext ctx = myRestfulServer.getFhirContext();
100
101                retVal = super.getServerConformance(theRequest, theRequestDetails);
102                for (Rest nextRest : retVal.getRest()) {
103
104                        for (RestResource nextResource : nextRest.getResource()) {
105
106                                ConditionalDeleteStatusEnum conditionalDelete = nextResource.getConditionalDeleteElement().getValueAsEnum();
107                                if (conditionalDelete == ConditionalDeleteStatusEnum.MULTIPLE_DELETES_SUPPORTED && myStorageSettings.isAllowMultipleDelete() == false) {
108                                        nextResource.setConditionalDelete(ConditionalDeleteStatusEnum.SINGLE_DELETES_SUPPORTED);
109                                }
110
111                                // Add resource counts
112                                Long count = counts.get(nextResource.getTypeElement().getValueAsString());
113                                if (count != null) {
114                                        nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count));
115                                }
116
117                                // Add chained params
118                                for (RestResourceSearchParam nextParam : nextResource.getSearchParam()) {
119                                        if (nextParam.getTypeElement().getValueAsEnum() == SearchParamTypeEnum.REFERENCE) {
120                                                List<BoundCodeDt<ResourceTypeEnum>> targets = nextParam.getTarget();
121                                                for (BoundCodeDt<ResourceTypeEnum> next : targets) {
122                                                        RuntimeResourceDefinition def = ctx.getResourceDefinition(next.getValue());
123                                                        for (RuntimeSearchParam nextChainedParam : def.getSearchParams()) {
124                                                                nextParam.addChain(nextChainedParam.getName());
125                                                        }
126                                                }
127                                        }
128                                }
129
130                        }
131                }
132
133                if (myStorageSettings.getSupportedSubscriptionTypes().contains(Subscription.SubscriptionChannelType.WEBSOCKET)) {
134                        if (isNotBlank(myStorageSettings.getWebsocketContextPath())) {
135                                ExtensionDt websocketExtension = new ExtensionDt();
136                                websocketExtension.setUrl(Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL);
137                                websocketExtension.setValue(new UriDt(myStorageSettings.getWebsocketContextPath()));
138                                retVal.getRestFirstRep().addUndeclaredExtension(websocketExtension);
139                        }
140                }
141
142                if (isNotBlank(myImplementationDescription)) {
143                        retVal.getImplementation().setDescription(myImplementationDescription);
144                }
145                myCachedValue = retVal;
146                return retVal;
147        }
148
149        public boolean isIncludeResourceCounts() {
150                return myIncludeResourceCounts;
151        }
152
153        public void setStorageSettings(JpaStorageSettings theStorageSettings) {
154                this.myStorageSettings = theStorageSettings;
155        }
156
157        @CoverageIgnore
158        public void setImplementationDescription(String theImplDesc) {
159                myImplementationDescription = theImplDesc;
160        }
161
162        public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
163                myIncludeResourceCounts = theIncludeResourceCounts;
164        }
165
166        @Override
167        public void setRestfulServer(RestfulServer theRestfulServer) {
168                this.myRestfulServer = theRestfulServer;
169                super.setRestfulServer(theRestfulServer);
170        }
171
172        @CoverageIgnore
173        public void setSystemDao(IFhirSystemDao<Bundle, MetaDt> mySystemDao) {
174                this.mySystemDao = mySystemDao;
175        }
176}