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}