
001/* 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2025 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 ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT; 046import static ca.uhn.fhir.rest.api.Constants.PARAM_FILTER; 047import static ca.uhn.fhir.rest.api.Constants.PARAM_LANGUAGE; 048import static ca.uhn.fhir.rest.api.Constants.PARAM_TEXT; 049import static org.apache.commons.lang3.StringUtils.isNotBlank; 050 051/** 052 * R4+ Only 053 */ 054public class JpaCapabilityStatementProvider extends ServerCapabilityStatementProvider { 055 056 private final FhirContext myContext; 057 private final ISearchParamRegistry mySearchParamRegistry; 058 private JpaStorageSettings myStorageSettings; 059 private String myImplementationDescription; 060 private boolean myIncludeResourceCounts; 061 private IFhirSystemDao<?, ?> mySystemDao; 062 063 /** 064 * Constructor 065 */ 066 public JpaCapabilityStatementProvider( 067 @Nonnull RestfulServer theRestfulServer, 068 @Nonnull IFhirSystemDao<?, ?> theSystemDao, 069 @Nonnull JpaStorageSettings theStorageSettings, 070 @Nonnull ISearchParamRegistry theSearchParamRegistry, 071 IValidationSupport theValidationSupport) { 072 super(theRestfulServer, theSearchParamRegistry, theValidationSupport); 073 074 Validate.notNull(theRestfulServer, "theRestfulServer must not be null"); 075 Validate.notNull(theSystemDao, "theSystemDao must not be null"); 076 Validate.notNull(theStorageSettings, "theStorageSettings must not be null"); 077 Validate.notNull(theSearchParamRegistry, "theSearchParamRegistry must not be null"); 078 079 myContext = theRestfulServer.getFhirContext(); 080 mySystemDao = theSystemDao; 081 myStorageSettings = theStorageSettings; 082 mySearchParamRegistry = theSearchParamRegistry; 083 084 setIncludeResourceCounts(true); 085 } 086 087 @Override 088 protected void postProcess(FhirTerser theTerser, IBaseConformance theCapabilityStatement) { 089 super.postProcess(theTerser, theCapabilityStatement); 090 091 if (isNotBlank(myImplementationDescription)) { 092 theTerser.setElement(theCapabilityStatement, "implementation.description", myImplementationDescription); 093 } 094 095 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_JSON_NEW); 096 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_XML_NEW); 097 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_JSON_PATCH); 098 theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_XML_PATCH); 099 } 100 101 @Override 102 protected void postProcessRestResource(FhirTerser theTerser, IBase theResource, String theResourceName) { 103 super.postProcessRestResource(theTerser, theResource, theResourceName); 104 105 theTerser.addElement(theResource, "versioning", ResourceVersionPolicy.VERSIONEDUPDATE.toCode()); 106 107 if (myStorageSettings.isAllowMultipleDelete()) { 108 theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.MULTIPLE.toCode()); 109 } else { 110 theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.SINGLE.toCode()); 111 } 112 113 // Add resource counts 114 if (myIncludeResourceCounts) { 115 Map<String, Long> counts = mySystemDao.getResourceCountsFromCache(); 116 if (counts != null) { 117 Long count = counts.get(theResourceName); 118 if (count != null) { 119 ExtensionUtil.setExtension( 120 myContext, 121 theResource, 122 ExtensionConstants.CONF_RESOURCE_COUNT, 123 "decimal", 124 Long.toString(count)); 125 } 126 } 127 } 128 } 129 130 public boolean isIncludeResourceCounts() { 131 return myIncludeResourceCounts; 132 } 133 134 public void setIncludeResourceCounts(boolean theIncludeResourceCounts) { 135 myIncludeResourceCounts = theIncludeResourceCounts; 136 } 137 138 public void setStorageSettings(JpaStorageSettings theStorageSettings) { 139 this.myStorageSettings = theStorageSettings; 140 } 141 142 @CoverageIgnore 143 public void setImplementationDescription(String theImplDesc) { 144 myImplementationDescription = theImplDesc; 145 } 146 147 @CoverageIgnore 148 public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) { 149 this.mySystemDao = mySystemDao; 150 } 151 152 @Override 153 protected boolean searchParamEnabled(String theResourceName, String theSearchParam) { 154 return switch (theSearchParam) { 155 case PARAM_FILTER -> myStorageSettings.isFilterParameterEnabled(); 156 case PARAM_CONTENT, PARAM_TEXT, PARAM_LANGUAGE -> mySearchParamRegistry.hasActiveSearchParam( 157 theResourceName, theSearchParam, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH); 158 default -> true; 159 }; 160 } 161}