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}