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 postProcessRestResource(FhirTerser theTerser, IBase theResource, String theResourceName) { 096 super.postProcessRestResource(theTerser, theResource, theResourceName); 097 098 theTerser.addElement(theResource, "versioning", ResourceVersionPolicy.VERSIONEDUPDATE.toCode()); 099 100 if (myStorageSettings.isAllowMultipleDelete()) { 101 theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.MULTIPLE.toCode()); 102 } else { 103 theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.SINGLE.toCode()); 104 } 105 106 // Add resource counts 107 if (myIncludeResourceCounts) { 108 Map<String, Long> counts = mySystemDao.getResourceCountsFromCache(); 109 if (counts != null) { 110 Long count = counts.get(theResourceName); 111 if (count != null) { 112 ExtensionUtil.setExtension( 113 myContext, 114 theResource, 115 ExtensionConstants.CONF_RESOURCE_COUNT, 116 "decimal", 117 Long.toString(count)); 118 } 119 } 120 } 121 } 122 123 public boolean isIncludeResourceCounts() { 124 return myIncludeResourceCounts; 125 } 126 127 public void setIncludeResourceCounts(boolean theIncludeResourceCounts) { 128 myIncludeResourceCounts = theIncludeResourceCounts; 129 } 130 131 public void setStorageSettings(JpaStorageSettings theStorageSettings) { 132 this.myStorageSettings = theStorageSettings; 133 } 134 135 @CoverageIgnore 136 public void setImplementationDescription(String theImplDesc) { 137 myImplementationDescription = theImplDesc; 138 } 139 140 @CoverageIgnore 141 public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) { 142 this.mySystemDao = mySystemDao; 143 } 144 145 @Override 146 protected boolean searchParamEnabled(String theSearchParam) { 147 return !Constants.PARAM_FILTER.equals(theSearchParam) || myStorageSettings.isFilterParameterEnabled(); 148 } 149}