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.support.IValidationSupport; 024import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; 025import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; 026import ca.uhn.fhir.rest.api.Constants; 027import ca.uhn.fhir.rest.server.RestfulServer; 028import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; 029import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; 030import ca.uhn.fhir.util.CoverageIgnore; 031import ca.uhn.fhir.util.ExtensionConstants; 032import ca.uhn.fhir.util.ExtensionUtil; 033import ca.uhn.fhir.util.FhirTerser; 034import jakarta.annotation.Nonnull; 035import org.apache.commons.lang3.Validate; 036import org.hl7.fhir.instance.model.api.IBase; 037import org.hl7.fhir.instance.model.api.IBaseConformance; 038import org.hl7.fhir.r4.model.Bundle; 039import org.hl7.fhir.r4.model.CapabilityStatement.ConditionalDeleteStatus; 040import org.hl7.fhir.r4.model.CapabilityStatement.ResourceVersionPolicy; 041import org.hl7.fhir.r4.model.Meta; 042 043import java.util.Map; 044 045import static org.apache.commons.lang3.StringUtils.isNotBlank; 046 047/** 048 * R4+ Only 049 */ 050public class JpaCapabilityStatementProvider extends ServerCapabilityStatementProvider { 051 052 private final FhirContext myContext; 053 private JpaStorageSettings myStorageSettings; 054 private String myImplementationDescription; 055 private boolean myIncludeResourceCounts; 056 private IFhirSystemDao<?, ?> mySystemDao; 057 058 /** 059 * Constructor 060 */ 061 public JpaCapabilityStatementProvider( 062 @Nonnull RestfulServer theRestfulServer, 063 @Nonnull IFhirSystemDao<?, ?> theSystemDao, 064 @Nonnull JpaStorageSettings theStorageSettings, 065 @Nonnull ISearchParamRegistry theSearchParamRegistry, 066 IValidationSupport theValidationSupport) { 067 super(theRestfulServer, theSearchParamRegistry, theValidationSupport); 068 069 Validate.notNull(theRestfulServer); 070 Validate.notNull(theSystemDao); 071 Validate.notNull(theStorageSettings); 072 Validate.notNull(theSearchParamRegistry); 073 074 myContext = theRestfulServer.getFhirContext(); 075 mySystemDao = theSystemDao; 076 myStorageSettings = theStorageSettings; 077 setIncludeResourceCounts(true); 078 } 079 080 @Override 081 protected void postProcess(FhirTerser theTerser, IBaseConformance theCapabilityStatement) { 082 super.postProcess(theTerser, theCapabilityStatement); 083 084 if (isNotBlank(myImplementationDescription)) { 085 theTerser.setElement(theCapabilityStatement, "implementation.description", myImplementationDescription); 086 } 087 088 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_JSON_NEW); 089 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_XML_NEW); 090 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_JSON_PATCH); 091 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_XML_PATCH); 092 } 093 094 @Override 095 protected void postProcessRest(FhirTerser theTerser, IBase theRest) { 096 super.postProcessRest(theTerser, theRest); 097 098 if (myStorageSettings 099 .getSupportedSubscriptionTypes() 100 .contains(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET)) { 101 if (isNotBlank(myStorageSettings.getWebsocketContextPath())) { 102 ExtensionUtil.setExtension( 103 myContext, 104 theRest, 105 Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL, 106 "uri", 107 myStorageSettings.getWebsocketContextPath()); 108 } 109 } 110 } 111 112 @Override 113 protected void postProcessRestResource(FhirTerser theTerser, IBase theResource, String theResourceName) { 114 super.postProcessRestResource(theTerser, theResource, theResourceName); 115 116 theTerser.addElement(theResource, "versioning", ResourceVersionPolicy.VERSIONEDUPDATE.toCode()); 117 118 if (myStorageSettings.isAllowMultipleDelete()) { 119 theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.MULTIPLE.toCode()); 120 } else { 121 theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.SINGLE.toCode()); 122 } 123 124 // Add resource counts 125 if (myIncludeResourceCounts) { 126 Map<String, Long> counts = mySystemDao.getResourceCountsFromCache(); 127 if (counts != null) { 128 Long count = counts.get(theResourceName); 129 if (count != null) { 130 ExtensionUtil.setExtension( 131 myContext, 132 theResource, 133 ExtensionConstants.CONF_RESOURCE_COUNT, 134 "decimal", 135 Long.toString(count)); 136 } 137 } 138 } 139 } 140 141 public boolean isIncludeResourceCounts() { 142 return myIncludeResourceCounts; 143 } 144 145 public void setIncludeResourceCounts(boolean theIncludeResourceCounts) { 146 myIncludeResourceCounts = theIncludeResourceCounts; 147 } 148 149 public void setStorageSettings(JpaStorageSettings theStorageSettings) { 150 this.myStorageSettings = theStorageSettings; 151 } 152 153 @CoverageIgnore 154 public void setImplementationDescription(String theImplDesc) { 155 myImplementationDescription = theImplDesc; 156 } 157 158 @CoverageIgnore 159 public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) { 160 this.mySystemDao = mySystemDao; 161 } 162 163 @Override 164 protected boolean searchParamEnabled(String theSearchParam) { 165 return !Constants.PARAM_FILTER.equals(theSearchParam) || myStorageSettings.isFilterParameterEnabled(); 166 } 167}