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        /**
082         * Copy constructor
083         * @param theOther The request details to copy from
084         */
085        public SystemRequestDetails(SystemRequestDetails theOther) {
086                super(theOther);
087                if (nonNull(theOther.getServer())) {
088                        myServer = theOther.getServer();
089                        myFhirContext = theOther.getFhirContext();
090                }
091                if (nonNull(theOther.myHeaders)) {
092                        initHeaderMap();
093                        myHeaders.putAll(theOther.myHeaders);
094                }
095                myRequestPartitionId = theOther.myRequestPartitionId;
096        }
097
098        // TODO KHS use this everywhere we create a srd with only one partition
099        public static SystemRequestDetails forRequestPartitionId(RequestPartitionId thePartitionId) {
100                SystemRequestDetails retVal = new SystemRequestDetails();
101                retVal.setRequestPartitionId(thePartitionId);
102                return retVal;
103        }
104
105        public RequestPartitionId getRequestPartitionId() {
106                return myRequestPartitionId;
107        }
108
109        public SystemRequestDetails setRequestPartitionId(RequestPartitionId theRequestPartitionId) {
110                myRequestPartitionId = theRequestPartitionId;
111                return this;
112        }
113
114        @Override
115        protected byte[] getByteStreamRequestContents() {
116                return new byte[0];
117        }
118
119        @Override
120        public Charset getCharset() {
121                return null;
122        }
123
124        @Override
125        public FhirContext getFhirContext() {
126                return myFhirContext;
127        }
128
129        public void setFhirContext(FhirContext theFhirContext) {
130                myFhirContext = theFhirContext;
131        }
132
133        @Override
134        public String getHeader(String name) {
135                List<String> headers = getHeaders(name);
136                if (headers.isEmpty()) {
137                        return null;
138                } else {
139                        return headers.get(0);
140                }
141        }
142
143        @Override
144        public List<String> getHeaders(String name) {
145                ListMultimap<String, String> headers = myHeaders;
146                if (headers == null) {
147                        headers = ImmutableListMultimap.of();
148                }
149                return headers.get(name);
150        }
151
152        @Override
153        public void addHeader(String theName, String theValue) {
154                initHeaderMap();
155                myHeaders.put(theName, theValue);
156        }
157
158        @Override
159        public void setHeaders(String theName, List<String> theValues) {
160                initHeaderMap();
161                myHeaders.putAll(theName, theValues);
162        }
163
164        private void initHeaderMap() {
165                if (myHeaders == null) {
166                        // Make sure we are case-insensitive on keys
167                        myHeaders = MultimapBuilder.treeKeys(String.CASE_INSENSITIVE_ORDER)
168                                        .arrayListValues()
169                                        .build();
170                }
171        }
172
173        @Override
174        public Object getAttribute(String theAttributeName) {
175                return null;
176        }
177
178        @Override
179        public void setAttribute(String theAttributeName, Object theAttributeValue) {}
180
181        @Override
182        public InputStream getInputStream() throws IOException {
183                return null;
184        }
185
186        @Override
187        public Reader getReader() {
188                return null;
189        }
190
191        @Override
192        public IRestfulServerDefaults getServer() {
193                return myServer;
194        }
195
196        public void setServer(RestfulServer theServer) {
197                this.myServer = theServer;
198        }
199
200        @Override
201        public String getServerBaseForRequest() {
202                return null;
203        }
204
205        private static class MyRestfulServerDefaults implements IRestfulServerDefaults {
206
207                @Override
208                public AddProfileTagEnum getAddProfileTag() {
209                        return null;
210                }
211
212                @Override
213                public EncodingEnum getDefaultResponseEncoding() {
214                        return null;
215                }
216
217                @Override
218                public ETagSupportEnum getETagSupport() {
219                        return null;
220                }
221
222                @Override
223                public ElementsSupportEnum getElementsSupport() {
224                        return null;
225                }
226
227                @Override
228                public FhirContext getFhirContext() {
229                        return null;
230                }
231
232                @Override
233                public List<IServerInterceptor> getInterceptors_() {
234                        return null;
235                }
236
237                @Override
238                public IPagingProvider getPagingProvider() {
239                        return null;
240                }
241
242                @Override
243                public boolean isDefaultPrettyPrint() {
244                        return false;
245                }
246
247                @Override
248                public IInterceptorService getInterceptorService() {
249                        return null;
250                }
251        }
252
253        private static class MyInterceptorBroadcaster implements IInterceptorBroadcaster {
254
255                @Override
256                public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
257                        return true;
258                }
259
260                @Override
261                public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
262                        return null;
263                }
264
265                @Override
266                public boolean hasHooks(Pointcut thePointcut) {
267                        return false;
268                }
269
270                @Override
271                public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) {
272                        return Collections.emptyList();
273                }
274        }
275
276        public static SystemRequestDetails forAllPartitions() {
277                return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions());
278        }
279
280        public static SystemRequestDetails newSystemRequestAllPartitions() {
281                SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
282                systemRequestDetails.setRequestPartitionId(RequestPartitionId.allPartitions());
283                return systemRequestDetails;
284        }
285}