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}