001/*-
002 * #%L
003 * HAPI FHIR - Server Framework
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.rest.api.server;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.context.api.AddProfileTagEnum;
024import ca.uhn.fhir.interceptor.api.HookParams;
025import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
026import ca.uhn.fhir.interceptor.api.IInterceptorService;
027import ca.uhn.fhir.interceptor.api.Pointcut;
028import ca.uhn.fhir.interceptor.model.RequestPartitionId;
029import ca.uhn.fhir.rest.api.EncodingEnum;
030import ca.uhn.fhir.rest.server.ETagSupportEnum;
031import ca.uhn.fhir.rest.server.ElementsSupportEnum;
032import ca.uhn.fhir.rest.server.IPagingProvider;
033import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
034import ca.uhn.fhir.rest.server.RestfulServer;
035import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
036import com.google.common.collect.ImmutableListMultimap;
037import com.google.common.collect.ListMultimap;
038import com.google.common.collect.MultimapBuilder;
039
040import java.io.IOException;
041import java.io.InputStream;
042import java.io.Reader;
043import java.nio.charset.Charset;
044import java.util.Collections;
045import java.util.List;
046
047import static java.util.Objects.nonNull;
048
049/**
050 * A default RequestDetails implementation that can be used for system calls to
051 * Resource DAO methods when partitioning is enabled. Using a SystemRequestDetails
052 * instance for system calls will ensure that any resource queries or updates will
053 * use the DEFAULT partition when partitioning is enabled.
054 */
055public class SystemRequestDetails extends RequestDetails {
056        private FhirContext myFhirContext;
057        private ListMultimap<String, String> myHeaders;
058        /**
059         * If a SystemRequestDetails has a RequestPartitionId, it will take precedence over the tenantId
060         */
061        private RequestPartitionId myRequestPartitionId;
062
063        private IRestfulServerDefaults myServer = new MyRestfulServerDefaults();
064
065        public SystemRequestDetails() {
066                this(new MyInterceptorBroadcaster());
067        }
068
069        public SystemRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) {
070                super(theInterceptorBroadcaster);
071        }
072
073        public SystemRequestDetails(RequestDetails theDetails) {
074                super(theDetails);
075                if (nonNull(theDetails.getServer())) {
076                        myServer = theDetails.getServer();
077                        myFhirContext = theDetails.getFhirContext();
078                }
079        }
080
081        // TODO KHS use this everywhere we create a srd with only one partition
082        public static SystemRequestDetails forRequestPartitionId(RequestPartitionId thePartitionId) {
083                SystemRequestDetails retVal = new SystemRequestDetails();
084                retVal.setRequestPartitionId(thePartitionId);
085                return retVal;
086        }
087
088        public RequestPartitionId getRequestPartitionId() {
089                return myRequestPartitionId;
090        }
091
092        public SystemRequestDetails setRequestPartitionId(RequestPartitionId theRequestPartitionId) {
093                myRequestPartitionId = theRequestPartitionId;
094                return this;
095        }
096
097        @Override
098        protected byte[] getByteStreamRequestContents() {
099                return new byte[0];
100        }
101
102        @Override
103        public Charset getCharset() {
104                return null;
105        }
106
107        @Override
108        public FhirContext getFhirContext() {
109                return myFhirContext;
110        }
111
112        public void setFhirContext(FhirContext theFhirContext) {
113                myFhirContext = theFhirContext;
114        }
115
116        @Override
117        public String getHeader(String name) {
118                List<String> headers = getHeaders(name);
119                if (headers.isEmpty()) {
120                        return null;
121                } else {
122                        return headers.get(0);
123                }
124        }
125
126        @Override
127        public List<String> getHeaders(String name) {
128                ListMultimap<String, String> headers = myHeaders;
129                if (headers == null) {
130                        headers = ImmutableListMultimap.of();
131                }
132                return headers.get(name);
133        }
134
135        @Override
136        public void addHeader(String theName, String theValue) {
137                initHeaderMap();
138                myHeaders.put(theName, theValue);
139        }
140
141        @Override
142        public void setHeaders(String theName, List<String> theValues) {
143                initHeaderMap();
144                myHeaders.putAll(theName, theValues);
145        }
146
147        private void initHeaderMap() {
148                if (myHeaders == null) {
149                        // Make sure we are case-insensitive on keys
150                        myHeaders = MultimapBuilder.treeKeys(String.CASE_INSENSITIVE_ORDER)
151                                        .arrayListValues()
152                                        .build();
153                }
154        }
155
156        @Override
157        public Object getAttribute(String theAttributeName) {
158                return null;
159        }
160
161        @Override
162        public void setAttribute(String theAttributeName, Object theAttributeValue) {}
163
164        @Override
165        public InputStream getInputStream() throws IOException {
166                return null;
167        }
168
169        @Override
170        public Reader getReader() {
171                return null;
172        }
173
174        @Override
175        public IRestfulServerDefaults getServer() {
176                return myServer;
177        }
178
179        public void setServer(RestfulServer theServer) {
180                this.myServer = theServer;
181        }
182
183        @Override
184        public String getServerBaseForRequest() {
185                return null;
186        }
187
188        private static class MyRestfulServerDefaults implements IRestfulServerDefaults {
189
190                @Override
191                public AddProfileTagEnum getAddProfileTag() {
192                        return null;
193                }
194
195                @Override
196                public EncodingEnum getDefaultResponseEncoding() {
197                        return null;
198                }
199
200                @Override
201                public ETagSupportEnum getETagSupport() {
202                        return null;
203                }
204
205                @Override
206                public ElementsSupportEnum getElementsSupport() {
207                        return null;
208                }
209
210                @Override
211                public FhirContext getFhirContext() {
212                        return null;
213                }
214
215                @Override
216                public List<IServerInterceptor> getInterceptors_() {
217                        return null;
218                }
219
220                @Override
221                public IPagingProvider getPagingProvider() {
222                        return null;
223                }
224
225                @Override
226                public boolean isDefaultPrettyPrint() {
227                        return false;
228                }
229
230                @Override
231                public IInterceptorService getInterceptorService() {
232                        return null;
233                }
234        }
235
236        private static class MyInterceptorBroadcaster implements IInterceptorBroadcaster {
237
238                @Override
239                public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
240                        return true;
241                }
242
243                @Override
244                public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
245                        return null;
246                }
247
248                @Override
249                public boolean hasHooks(Pointcut thePointcut) {
250                        return false;
251                }
252
253                @Override
254                public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) {
255                        return Collections.emptyList();
256                }
257        }
258
259        public static SystemRequestDetails forAllPartitions() {
260                return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions());
261        }
262
263        public static SystemRequestDetails newSystemRequestAllPartitions() {
264                SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
265                systemRequestDetails.setRequestPartitionId(RequestPartitionId.allPartitions());
266                return systemRequestDetails;
267        }
268}