001/*
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2023 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 org.apache.commons.lang3.Validate;
035import org.hl7.fhir.instance.model.api.IBase;
036import org.hl7.fhir.instance.model.api.IBaseConformance;
037import org.hl7.fhir.r4.model.Bundle;
038import org.hl7.fhir.r4.model.CapabilityStatement.ConditionalDeleteStatus;
039import org.hl7.fhir.r4.model.CapabilityStatement.ResourceVersionPolicy;
040import org.hl7.fhir.r4.model.Meta;
041
042import javax.annotation.Nonnull;
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(@Nonnull RestfulServer theRestfulServer, @Nonnull IFhirSystemDao<?, ?> theSystemDao, @Nonnull JpaStorageSettings theStorageSettings, @Nonnull ISearchParamRegistry theSearchParamRegistry, IValidationSupport theValidationSupport) {
062                super(theRestfulServer, theSearchParamRegistry, theValidationSupport);
063
064                Validate.notNull(theRestfulServer);
065                Validate.notNull(theSystemDao);
066                Validate.notNull(theStorageSettings);
067                Validate.notNull(theSearchParamRegistry);
068
069                myContext = theRestfulServer.getFhirContext();
070                mySystemDao = theSystemDao;
071                myStorageSettings = theStorageSettings;
072                setIncludeResourceCounts(true);
073        }
074
075        @Override
076        protected void postProcess(FhirTerser theTerser, IBaseConformance theCapabilityStatement) {
077                super.postProcess(theTerser, theCapabilityStatement);
078
079                if (isNotBlank(myImplementationDescription)) {
080                        theTerser.setElement(theCapabilityStatement, "implementation.description", myImplementationDescription);
081                }
082
083                theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_JSON_NEW);
084                theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_XML_NEW);
085                theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_JSON_PATCH);
086                theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_XML_PATCH);
087        }
088
089        @Override
090        protected void postProcessRest(FhirTerser theTerser, IBase theRest) {
091                super.postProcessRest(theTerser, theRest);
092
093                if (myStorageSettings.getSupportedSubscriptionTypes().contains(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET)) {
094                        if (isNotBlank(myStorageSettings.getWebsocketContextPath())) {
095                                ExtensionUtil.setExtension(myContext, theRest, Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL, "uri", myStorageSettings.getWebsocketContextPath());
096                        }
097                }
098
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(myContext, theResource, ExtensionConstants.CONF_RESOURCE_COUNT, "decimal", Long.toString(count));
120                                }
121                        }
122                }
123
124        }
125
126        public boolean isIncludeResourceCounts() {
127                return myIncludeResourceCounts;
128        }
129
130        public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
131                myIncludeResourceCounts = theIncludeResourceCounts;
132        }
133
134        public void setStorageSettings(JpaStorageSettings theStorageSettings) {
135                this.myStorageSettings = theStorageSettings;
136        }
137
138        @CoverageIgnore
139        public void setImplementationDescription(String theImplDesc) {
140                myImplementationDescription = theImplDesc;
141        }
142
143        @CoverageIgnore
144        public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) {
145                this.mySystemDao = mySystemDao;
146        }
147
148        @Override
149        protected boolean searchParamEnabled(String theSearchParam) {
150                return !Constants.PARAM_FILTER.equals(theSearchParam) || myStorageSettings.isFilterParameterEnabled();
151        }
152}