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