001package ca.uhn.fhir.jaxrs.server.util;
002
003/*
004 * #%L
005 * HAPI FHIR JAX-RS Server
006 * %%
007 * Copyright (C) 2014 - 2021 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.context.ConfigurationException;
024import ca.uhn.fhir.context.FhirContext;
025import ca.uhn.fhir.context.FhirVersionEnum;
026import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
027import ca.uhn.fhir.rest.api.Constants;
028import ca.uhn.fhir.rest.api.RequestTypeEnum;
029import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
030import ca.uhn.fhir.rest.api.server.IRestfulResponse;
031import ca.uhn.fhir.rest.api.server.RequestDetails;
032import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
033import ca.uhn.fhir.rest.server.method.ResourceParameter;
034import ca.uhn.fhir.util.UrlUtil;
035import org.apache.commons.lang3.StringUtils;
036
037import javax.ws.rs.core.HttpHeaders;
038import javax.ws.rs.core.MediaType;
039import java.io.IOException;
040import java.io.InputStream;
041import java.io.Reader;
042import java.nio.charset.Charset;
043import java.util.Collections;
044import java.util.HashMap;
045import java.util.List;
046import java.util.Map;
047
048/**
049 * The JaxRsRequest is a jax-rs specific implementation of the RequestDetails.
050 *
051 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
052 */
053public class JaxRsRequest extends RequestDetails {
054
055        private HttpHeaders myHeaders;
056        private String myResourceString;
057        private AbstractJaxRsProvider myServer;
058        private Map<String, Object> myAttributes = new HashMap<>();
059
060        /**
061         * Utility Constructor
062         *
063         * @param server         the server
064         * @param resourceString the resource body
065         * @param requestType    the request type
066         * @param restOperation  the operation type
067         */
068        public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
069                                                          RestOperationTypeEnum restOperation) {
070                super(server.getInterceptorService());
071                this.myHeaders = server.getHeaders();
072                this.myResourceString = resourceString;
073                this.setRestOperationType(restOperation);
074                setServer(server);
075                setFhirServerBase(server.getBaseForServer());
076                setParameters(server.getParameters());
077                setRequestType(requestType);
078        }
079
080        @Override
081        protected byte[] getByteStreamRequestContents() {
082                return StringUtils.defaultString(myResourceString, "")
083                        .getBytes(ResourceParameter.determineRequestCharset(this));
084        }
085
086        @Override
087        public Charset getCharset() {
088                String charset = null;
089
090                if (myHeaders.getMediaType() != null && myHeaders.getMediaType().getParameters() != null) {
091                        charset = myHeaders.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER);
092                }
093                if (charset != null) {
094                        return Charset.forName(charset);
095                } else {
096                        return null;
097                }
098        }
099
100        @Override
101        public FhirContext getFhirContext() {
102                return myServer.getFhirContext();
103        }
104
105        @Override
106        public String getHeader(String headerKey) {
107                List<String> requestHeader = getHeaders(headerKey);
108                return requestHeader.isEmpty() ? null : requestHeader.get(0);
109        }
110
111        @Override
112        public List<String> getHeaders(String name) {
113                List<String> requestHeader = myHeaders.getRequestHeader(name);
114                return requestHeader == null ? Collections.<String>emptyList() : requestHeader;
115        }
116
117        @Override
118        public Object getAttribute(String theAttributeName) {
119                return myAttributes.get(theAttributeName);
120        }
121
122        @Override
123        public void setAttribute(String theAttributeName, Object theAttributeValue) {
124                myAttributes.put(theAttributeName, theAttributeValue);
125        }
126
127        @Override
128        public InputStream getInputStream() {
129                // not yet implemented
130                throw new UnsupportedOperationException();
131        }
132
133        @Override
134        public Reader getReader() throws IOException {
135                // not yet implemented
136                throw new UnsupportedOperationException();
137        }
138
139        @Override
140        public IRestfulResponse getResponse() {
141                if (super.getResponse() == null) {
142                        setResponse(new JaxRsResponse(this));
143                }
144                return super.getResponse();
145        }
146
147        @Override
148        public AbstractJaxRsProvider getServer() {
149                return myServer;
150        }
151
152        /**
153         * Set the server
154         *
155         * @param theServer the server to set
156         */
157        public void setServer(AbstractJaxRsProvider theServer) {
158                this.myServer = theServer;
159        }
160
161        @Override
162        public String getServerBaseForRequest() {
163                return getServer().getServerAddressStrategy().determineServerBase(null, null);
164        }
165
166        /**
167         * An implementation of the builder pattern for the JaxRsRequest
168         */
169        public static class Builder {
170                private final String myResourceName;
171                private String myCompartment;
172                private String myId;
173                private RequestTypeEnum myRequestType;
174                private String myRequestUrl;
175                private String myResource;
176                private RestOperationTypeEnum myRestOperation;
177                private AbstractJaxRsProvider myServer;
178                private String myVersion;
179
180                /**
181                 * Utility Constructor
182                 *
183                 * @param theServer        the server
184                 * @param theRequestType   the request type
185                 * @param theRestOperation the rest operation
186                 * @param theRequestUrl
187                 */
188                public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType,
189                                                        RestOperationTypeEnum theRestOperation, String theRequestUrl, String theResourceName) {
190                        this.myServer = theServer;
191                        this.myRequestType = theRequestType;
192                        this.myRestOperation = theRestOperation;
193                        this.myRequestUrl = theRequestUrl;
194                        this.myResourceName = theResourceName;
195                }
196
197                /**
198                 * Create the jax-rs request
199                 *
200                 * @return the jax-rs request
201                 */
202                public JaxRsRequest build() {
203                        JaxRsRequest result = new JaxRsRequest(myServer, myResource, myRequestType, myRestOperation);
204                        if ((StringUtils.isNotBlank(myVersion) || StringUtils.isNotBlank(myCompartment))
205                                && StringUtils.isBlank(myId)) {
206                                throw new InvalidRequestException("Don't know how to handle request path: "
207                                        + myServer.getUriInfo().getRequestUri().toASCIIString());
208                        }
209
210                        FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion();
211
212                        if (StringUtils.isNotBlank(myVersion)) {
213                                switch (fhirContextVersion) {
214                                        case R4:
215                                                result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
216                                                break;
217                                        case DSTU3:
218                                                result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
219                                                break;
220                                        case DSTU2_1:
221                                                result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
222                                                break;
223                                        case DSTU2_HL7ORG:
224                                                result.setId(new org.hl7.fhir.dstu2.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
225                                                break;
226                                        case DSTU2:
227                                                result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
228                                                break;
229                                        default:
230                                                throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
231                                }
232                        } else if (StringUtils.isNotBlank(myId)) {
233                                switch (fhirContextVersion) {
234                                        case R4:
235                                                result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
236                                                break;
237                                        case DSTU3:
238                                                result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
239                                                break;
240                                        case DSTU2_1:
241                                                result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
242                                                break;
243                                        case DSTU2_HL7ORG:
244                                                result.setId(new org.hl7.fhir.dstu2.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
245                                                break;
246                                        case DSTU2:
247                                                result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
248                                                break;
249                                        default:
250                                                throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
251                                }
252                        }
253
254                        if (myRestOperation == RestOperationTypeEnum.UPDATE) {
255                                String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
256                                if (contentLocation != null) {
257                                        switch (fhirContextVersion) {
258                                                case R4:
259                                                        result.setId(new org.hl7.fhir.r4.model.IdType(contentLocation));
260                                                        break;
261                                                case DSTU3:
262                                                        result.setId(new org.hl7.fhir.dstu3.model.IdType(contentLocation));
263                                                        break;
264                                                case DSTU2_1:
265                                                        result.setId(new org.hl7.fhir.dstu2016may.model.IdType(contentLocation));
266                                                        break;
267                                                case DSTU2_HL7ORG:
268                                                        result.setId(new org.hl7.fhir.dstu2.model.IdType(contentLocation));
269                                                        break;
270                                                case DSTU2:
271                                                        result.setId(new ca.uhn.fhir.model.primitive.IdDt(contentLocation));
272                                                        break;
273                                                default:
274                                                        throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
275                                        }
276                                }
277                        }
278
279                        result.setCompartmentName(myCompartment);
280                        result.setCompleteUrl(myRequestUrl);
281                        result.setResourceName(myResourceName);
282
283                        return result;
284                }
285
286                /**
287                 * Set the compartment
288                 *
289                 * @param compartment the compartment
290                 * @return the builder
291                 */
292                public Builder compartment(String compartment) {
293                        this.myCompartment = compartment;
294                        return this;
295                }
296
297                /**
298                 * Set the id
299                 *
300                 * @param id the resource id
301                 * @return the builder
302                 */
303                public Builder id(String id) {
304                        this.myId = id;
305                        return this;
306                }
307
308                /**
309                 * Set the resource
310                 *
311                 * @param resource the body contents of an http method
312                 * @return the builder
313                 */
314                public Builder resource(String resource) {
315                        this.myResource = resource;
316                        return this;
317                }
318
319                /**
320                 * Set the id version
321                 *
322                 * @param version the version of the resource
323                 * @return the builder
324                 */
325                public Builder version(String version) {
326                        this.myVersion = version;
327                        return this;
328                }
329        }
330}