001/*-
002 * #%L
003 * HAPI FHIR Storage api
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.jpa.dao.dstu3;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
025import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
026import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
027import ca.uhn.fhir.util.BundleUtil;
028import org.hl7.fhir.dstu3.model.Bundle;
029import org.hl7.fhir.dstu3.model.OperationOutcome;
030import org.hl7.fhir.dstu3.model.Resource;
031import org.hl7.fhir.exceptions.FHIRException;
032import org.hl7.fhir.instance.model.api.IBaseExtension;
033import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
034import org.hl7.fhir.instance.model.api.IBaseResource;
035
036import java.util.Date;
037import java.util.List;
038import java.util.Optional;
039
040import static org.apache.commons.lang3.StringUtils.isBlank;
041
042public class TransactionProcessorVersionAdapterDstu3
043                implements ITransactionProcessorVersionAdapter<Bundle, Bundle.BundleEntryComponent> {
044        @Override
045        public void setResponseStatus(Bundle.BundleEntryComponent theBundleEntry, String theStatus) {
046                theBundleEntry.getResponse().setStatus(theStatus);
047        }
048
049        @Override
050        public void setResponseLastModified(Bundle.BundleEntryComponent theBundleEntry, Date theLastModified) {
051                theBundleEntry.getResponse().setLastModified(theLastModified);
052        }
053
054        @Override
055        public void setResource(Bundle.BundleEntryComponent theBundleEntry, IBaseResource theResource) {
056                theBundleEntry.setResource((Resource) theResource);
057        }
058
059        @Override
060        public IBaseResource getResource(Bundle.BundleEntryComponent theBundleEntry) {
061                return theBundleEntry.getResource();
062        }
063
064        @Override
065        public String getBundleType(Bundle theRequest) {
066                if (theRequest.getType() == null) {
067                        return null;
068                }
069                return theRequest.getTypeElement().getValue().toCode();
070        }
071
072        @Override
073        public void populateEntryWithOperationOutcome(
074                        BaseServerResponseException theCaughtEx, Bundle.BundleEntryComponent theEntry) {
075                OperationOutcome oo = new OperationOutcome();
076                oo.addIssue()
077                                .setSeverity(OperationOutcome.IssueSeverity.ERROR)
078                                .setDiagnostics(theCaughtEx.getMessage())
079                                .setCode(OperationOutcome.IssueType.EXCEPTION);
080                theEntry.getResponse().setOutcome(oo);
081        }
082
083        @Override
084        public Bundle createBundle(String theBundleType) {
085                Bundle resp = new Bundle();
086                try {
087                        resp.setType(Bundle.BundleType.fromCode(theBundleType));
088                } catch (FHIRException theE) {
089                        throw new InternalErrorException(Msg.code(548) + "Unknown bundle type: " + theBundleType);
090                }
091                return resp;
092        }
093
094        @Override
095        public List<Bundle.BundleEntryComponent> getEntries(Bundle theRequest) {
096                return theRequest.getEntry();
097        }
098
099        @Override
100        public void addEntry(Bundle theBundle, Bundle.BundleEntryComponent theEntry) {
101                theBundle.addEntry(theEntry);
102        }
103
104        @Override
105        public Bundle.BundleEntryComponent addEntry(Bundle theBundle) {
106                return theBundle.addEntry();
107        }
108
109        @Override
110        public String getEntryRequestVerb(FhirContext theContext, Bundle.BundleEntryComponent theEntry) {
111                String retVal = null;
112                Bundle.HTTPVerb value = theEntry.getRequest().getMethodElement().getValue();
113                if (value != null) {
114                        retVal = value.toCode();
115                }
116
117                /*
118                 * This is a workaround for the fact that PATCH isn't a valid constant for
119                 * DSTU3 Bundle.entry.request.method (it was added in R4)
120                 */
121                if (isBlank(retVal)) {
122                        Resource resource = theEntry.getResource();
123                        boolean isPatch = BundleUtil.isDstu3TransactionPatch(theContext, resource);
124
125                        if (isPatch) {
126                                retVal = "PATCH";
127                        }
128                }
129                return retVal;
130        }
131
132        @Override
133        public String getFullUrl(Bundle.BundleEntryComponent theEntry) {
134                return theEntry.getFullUrl();
135        }
136
137        @Override
138        public void setFullUrl(Bundle.BundleEntryComponent theEntry, String theFullUrl) {
139                theEntry.setFullUrl(theFullUrl);
140        }
141
142        @Override
143        public String getEntryIfNoneExist(Bundle.BundleEntryComponent theEntry) {
144                return theEntry.getRequest().getIfNoneExist();
145        }
146
147        @Override
148        public String getEntryRequestUrl(Bundle.BundleEntryComponent theEntry) {
149                return theEntry.getRequest().getUrl();
150        }
151
152        @Override
153        public void setResponseLocation(Bundle.BundleEntryComponent theEntry, String theResponseLocation) {
154                theEntry.getResponse().setLocation(theResponseLocation);
155        }
156
157        @Override
158        public void setResponseETag(Bundle.BundleEntryComponent theEntry, String theEtag) {
159                theEntry.getResponse().setEtag(theEtag);
160        }
161
162        @Override
163        public String getEntryRequestIfMatch(Bundle.BundleEntryComponent theEntry) {
164                return theEntry.getRequest().getIfMatch();
165        }
166
167        @Override
168        public String getEntryRequestIfNoneExist(Bundle.BundleEntryComponent theEntry) {
169                return theEntry.getRequest().getIfNoneExist();
170        }
171
172        @Override
173        public String getEntryRequestIfNoneMatch(Bundle.BundleEntryComponent theEntry) {
174                return theEntry.getRequest().getIfNoneMatch();
175        }
176
177        @Override
178        public void setResponseOutcome(Bundle.BundleEntryComponent theEntry, IBaseOperationOutcome theOperationOutcome) {
179                theEntry.getResponse().setOutcome((Resource) theOperationOutcome);
180        }
181
182        @Override
183        public void setRequestVerb(Bundle.BundleEntryComponent theEntry, String theVerb) {
184                theEntry.getRequest().setMethod(Bundle.HTTPVerb.fromCode(theVerb));
185        }
186
187        @Override
188        public void setRequestUrl(Bundle.BundleEntryComponent theEntry, String theUrl) {
189                theEntry.getRequest().setUrl(theUrl);
190        }
191
192        @Override
193        public Optional<IBaseExtension<?, ?>> getEntryRequestExtensionByUrl(
194                        Bundle.BundleEntryComponent theEntry, String theUrl) {
195                return Optional.ofNullable(theEntry.getRequest().getExtensionByUrl(theUrl));
196        }
197}