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