001package ca.uhn.hapi.converters.server;
002
003/*-
004 * #%L
005 * HAPI FHIR - Converter
006 * %%
007 * Copyright (C) 2014 - 2022 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.i18n.Msg;
024import ca.uhn.fhir.context.FhirContext;
025import ca.uhn.fhir.context.FhirVersionEnum;
026import ca.uhn.fhir.model.api.IResource;
027import ca.uhn.fhir.rest.api.Constants;
028import ca.uhn.fhir.rest.api.server.RequestDetails;
029import ca.uhn.fhir.rest.api.server.ResponseDetails;
030import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
031import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
032import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
033import org.hl7.fhir.converter.NullVersionConverterAdvisor10_30;
034import org.hl7.fhir.converter.NullVersionConverterAdvisor10_40;
035import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_30;
036import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_40;
037import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
038import org.hl7.fhir.dstu3.model.Resource;
039import org.hl7.fhir.exceptions.FHIRException;
040import org.hl7.fhir.instance.model.api.IBaseResource;
041
042import javax.servlet.http.HttpServletRequest;
043import javax.servlet.http.HttpServletResponse;
044import java.util.StringTokenizer;
045
046import static org.apache.commons.lang3.StringUtils.*;
047
048/**
049 * <b>This is an experimental interceptor! Use with caution as
050 * behaviour may change or be removed in a future version of
051 * FHIR.</b>
052 * <p>
053 * This interceptor partially implements the proposed
054 * Versioned API features.
055 * </p>
056 */
057public class VersionedApiConverterInterceptor extends InterceptorAdapter {
058        private final FhirContext myCtxDstu2;
059        private final FhirContext myCtxDstu2Hl7Org;
060        private final NullVersionConverterAdvisor10_40 advisor40;
061        private final NullVersionConverterAdvisor10_30 advisor30;
062
063        public VersionedApiConverterInterceptor() {
064                advisor40 = new NullVersionConverterAdvisor10_40();
065                advisor30 = new NullVersionConverterAdvisor10_30();
066
067                myCtxDstu2 = FhirContext.forDstu2();
068                myCtxDstu2Hl7Org = FhirContext.forDstu2Hl7Org();
069        }
070
071        @Override
072        public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
073                IBaseResource responseResource = theResponseDetails.getResponseResource();
074                if (responseResource == null) {
075                        return true;
076                }
077
078                String[] formatParams = theRequestDetails.getParameters().get(Constants.PARAM_FORMAT);
079                String accept = null;
080                if (formatParams != null && formatParams.length > 0) {
081                        accept = formatParams[0];
082                }
083                if (isBlank(accept)) {
084                        accept = defaultString(theServletRequest.getHeader(Constants.HEADER_ACCEPT));
085                }
086                StringTokenizer tok = new StringTokenizer(accept, ";");
087                String wantVersionString = null;
088                while (tok.hasMoreTokens()) {
089                        String next = tok.nextToken().trim();
090                        if (next.startsWith("fhirVersion=")) {
091                                wantVersionString = next.substring("fhirVersion=".length()).trim();
092                                break;
093                        }
094                }
095
096                FhirVersionEnum wantVersion = null;
097                if (isNotBlank(wantVersionString)) {
098                        wantVersion = FhirVersionEnum.forVersionString(wantVersionString);
099                }
100
101                FhirVersionEnum haveVersion = responseResource.getStructureFhirVersionEnum();
102
103                IBaseResource converted = null;
104                try {
105                        if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) {
106                                converted = VersionConvertorFactory_30_40.convertResource(toDstu3(responseResource));
107                        } else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) {
108                                converted = VersionConvertorFactory_30_40.convertResource(toR4(responseResource));
109                        } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.R4) {
110                                converted = VersionConvertorFactory_10_40.convertResource(toR4(responseResource), advisor40);
111                        } else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) {
112                                converted = VersionConvertorFactory_10_40.convertResource(toDstu2(responseResource), advisor40);
113                        } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.DSTU3) {
114                                converted = VersionConvertorFactory_10_30.convertResource(toDstu3(responseResource), advisor30);
115                        } else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.DSTU2) {
116                                converted = VersionConvertorFactory_10_30.convertResource(toDstu2(responseResource), advisor30);
117                        }
118                } catch (FHIRException e) {
119                        throw new InternalErrorException(Msg.code(73) + e);
120                }
121
122                if (converted != null) {
123                        theResponseDetails.setResponseResource(converted);
124                }
125
126                return true;
127        }
128
129        private org.hl7.fhir.dstu2.model.Resource toDstu2(IBaseResource theResponseResource) {
130                if (theResponseResource instanceof IResource) {
131                        return (org.hl7.fhir.dstu2.model.Resource) myCtxDstu2Hl7Org.newJsonParser().parseResource(myCtxDstu2.newJsonParser().encodeResourceToString(theResponseResource));
132                }
133                return (org.hl7.fhir.dstu2.model.Resource) theResponseResource;
134        }
135
136        private Resource toDstu3(IBaseResource theResponseResource) {
137                return (Resource) theResponseResource;
138        }
139
140        private org.hl7.fhir.r4.model.Resource toR4(IBaseResource theResponseResource) {
141                return (org.hl7.fhir.r4.model.Resource) theResponseResource;
142        }
143}