001package ca.cdr.api.model.json;
002
003/*
004 * #%L
005 * Smile CDR - CDR
006 * %%
007 * Copyright (C) 2016 - 2025 Smile CDR, Inc.
008 * %%
009 * All rights reserved.
010 * #L%
011 */
012
013import ca.uhn.fhir.context.FhirContext;
014import ca.uhn.fhir.jpa.dao.TransactionUtil;
015import ca.uhn.fhir.model.api.StorageResponseCodeEnum;
016import ca.uhn.fhir.model.primitive.IdDt;
017import ca.uhn.fhir.util.FhirTerser;
018import com.fasterxml.jackson.annotation.JsonProperty;
019import jakarta.annotation.Nonnull;
020import jakarta.annotation.Nullable;
021import org.apache.commons.lang3.builder.CompareToBuilder;
022import org.hl7.fhir.instance.model.api.IBase;
023import org.hl7.fhir.instance.model.api.IBaseBundle;
024import org.hl7.fhir.instance.model.api.IBaseResource;
025import org.hl7.fhir.instance.model.api.IIdType;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import java.util.ArrayList;
030import java.util.List;
031
032import static org.apache.commons.lang3.StringUtils.isBlank;
033import static org.apache.commons.lang3.StringUtils.isNotBlank;
034
035public class TransactionLogStorageOutcomeJson
036                implements IModelJson, Comparable<TransactionLogStorageOutcomeJson>, Cloneable {
037        private static final Logger ourLog = LoggerFactory.getLogger(TransactionLogStorageOutcomeJson.class);
038
039        @JsonProperty("sourceUri")
040        private String mySourceUri;
041
042        @JsonProperty("sourceResourceType")
043        private String mySourceResourceType;
044
045        @JsonProperty("sourceResourceId")
046        private String mySourceResourceId;
047
048        @JsonProperty("targetResourceType")
049        private String myTargetResourceType;
050
051        @JsonProperty("targetResourceId")
052        private String myTargetResourceId;
053
054        @JsonProperty("targetResourceVersion")
055        private Long myTargetResourceVersion;
056
057        @JsonProperty("outcome")
058        private StorageResponseCodeEnum myOutcome;
059
060        @JsonProperty("errorMessage")
061        private String myErrorMessage;
062
063        public Long getTargetResourceVersion() {
064                return myTargetResourceVersion;
065        }
066
067        public TransactionLogStorageOutcomeJson setTargetResourceVersion(Long theTargetResourceVersion) {
068                myTargetResourceVersion = theTargetResourceVersion;
069                return this;
070        }
071
072        public String getErrorMessage() {
073                return myErrorMessage;
074        }
075
076        public TransactionLogStorageOutcomeJson setErrorMessage(String theErrorMessage) {
077                myErrorMessage = theErrorMessage;
078                return this;
079        }
080
081        public String getSourceUri() {
082                return mySourceUri;
083        }
084
085        public TransactionLogStorageOutcomeJson setSourceUri(String theSourceUri) {
086                mySourceUri = theSourceUri;
087                return this;
088        }
089
090        public String getTargetResourceType() {
091                return myTargetResourceType;
092        }
093
094        public TransactionLogStorageOutcomeJson setTargetResourceType(String theTargetResourceType) {
095                myTargetResourceType = theTargetResourceType;
096                return this;
097        }
098
099        public String getTargetResourceId() {
100                return myTargetResourceId;
101        }
102
103        public IIdType toTargetResourceIdType() {
104                String targetVersion = myTargetResourceVersion != null ? myTargetResourceVersion.toString() : null;
105                return new IdDt(myTargetResourceType, myTargetResourceId, targetVersion);
106        }
107
108        public TransactionLogStorageOutcomeJson setTargetResourceId(String theTargetResourceId) {
109                myTargetResourceId = theTargetResourceId;
110                return this;
111        }
112
113        public StorageResponseCodeEnum getOutcome() {
114                return myOutcome;
115        }
116
117        public TransactionLogStorageOutcomeJson setOutcome(StorageResponseCodeEnum theOutcome) {
118                myOutcome = theOutcome;
119                return this;
120        }
121
122        @Override
123        public int compareTo(@Nonnull TransactionLogStorageOutcomeJson o) {
124                return new CompareToBuilder()
125                                .append(mySourceUri, o.mySourceUri)
126                                .append(myTargetResourceType, o.myTargetResourceType)
127                                .append(myTargetResourceId, o.myTargetResourceId)
128                                .append(myOutcome, o.myOutcome)
129                                .append(myErrorMessage, o.myErrorMessage)
130                                .toComparison();
131        }
132
133        public String getSourceResourceType() {
134                return mySourceResourceType;
135        }
136
137        public TransactionLogStorageOutcomeJson setSourceResourceType(String theSourceResourceType) {
138                mySourceResourceType = theSourceResourceType;
139                return this;
140        }
141
142        public String getSourceResourceId() {
143                return mySourceResourceId;
144        }
145
146        public TransactionLogStorageOutcomeJson setSourceResourceId(String theSourceResourceId) {
147                mySourceResourceId = theSourceResourceId;
148                return this;
149        }
150
151        public IIdType toSourceResourceIdType() {
152                return new IdDt(mySourceResourceType, mySourceResourceId);
153        }
154
155        public static TransactionLogStorageOutcomeJson fromStorageOutcome(
156                        TransactionUtil.StorageOutcome theStorageOutcome) {
157
158                String sourceUri = theStorageOutcome.getRequestMetaSource();
159
160                boolean isFailure = theStorageOutcome.getStatusCode() >= 400 && theStorageOutcome.getStatusCode() <= 599;
161                String errorMessage = theStorageOutcome.getErrorMessage();
162                if (isBlank(errorMessage) && isFailure) {
163                        errorMessage = theStorageOutcome.getStatusMessage();
164                }
165
166                StorageResponseCodeEnum responseCode = theStorageOutcome.getStorageResponseCode();
167                IIdType sourceId = theStorageOutcome.getSourceId();
168                IIdType targetId = theStorageOutcome.getTargetId();
169
170                TransactionLogStorageOutcomeJson logStorageOutcome = new TransactionLogStorageOutcomeJson();
171                logStorageOutcome.setOutcome(responseCode);
172                logStorageOutcome.setErrorMessage(errorMessage);
173                if (targetId != null) {
174                        logStorageOutcome.setTargetResourceType(targetId.getResourceType());
175                        logStorageOutcome.setTargetResourceId(targetId.getIdPart());
176                        if (targetId.isVersionIdPartValidLong()) {
177                                logStorageOutcome.setTargetResourceVersion(targetId.getVersionIdPartAsLong());
178                        }
179                }
180                if (sourceId != null) {
181                        logStorageOutcome.setSourceResourceType(sourceId.getResourceType());
182                        logStorageOutcome.setSourceResourceId(sourceId.getIdPart());
183                }
184                logStorageOutcome.setSourceUri(sourceUri);
185                return logStorageOutcome;
186        }
187
188        public static List<TransactionLogStorageOutcomeJson> processResponseBundleForStorageOutcomes(
189                        @Nonnull FhirContext theFhirContext,
190                        @Nullable IBaseBundle theRequest,
191                        @Nullable IBaseBundle theResponse,
192                        String theGlobalErrorMessage) {
193                List<TransactionLogStorageOutcomeJson> retVal = new ArrayList<>();
194
195                FhirTerser terser = theFhirContext.newTerser();
196
197                if (isNotBlank(theGlobalErrorMessage)) {
198                        String requestBundleSource = terser.getSinglePrimitiveValueOrNull(theRequest, "Bundle.meta.source");
199                        List<IBase> requestEntries = terser.getValues(theRequest, "entry");
200                        int count = requestEntries.size();
201
202                        for (int i = 0; i < count; i++) {
203                                IBase requestEntry = requestEntries.get(i);
204                                IIdType targetId = terser.getSingleValue(requestEntry, "resource", IBaseResource.class)
205                                                .map(IBaseResource::getIdElement)
206                                                .orElse(null);
207                                if (targetId != null) {
208                                        TransactionLogStorageOutcomeJson storageOutcome = new TransactionLogStorageOutcomeJson();
209                                        storageOutcome.setSourceUri(requestBundleSource);
210                                        storageOutcome.setOutcome(StorageResponseCodeEnum.FAILURE);
211                                        storageOutcome.setErrorMessage(theGlobalErrorMessage);
212                                        storageOutcome.setTargetResourceType(targetId.getResourceType());
213                                        storageOutcome.setTargetResourceId(targetId.getIdPart());
214                                        retVal.add(storageOutcome);
215                                }
216                        }
217                        return retVal;
218                }
219
220                if (theResponse == null) {
221                        // This shouldn't happen
222                        ourLog.warn("No response Bundle provided but no error message was present");
223                        return retVal;
224                }
225
226                TransactionUtil.TransactionResponse transactionResponse =
227                                TransactionUtil.parseTransactionResponse(theFhirContext, theRequest, theResponse);
228                for (TransactionUtil.StorageOutcome storageOutcome : transactionResponse.getStorageOutcomes()) {
229                        TransactionLogStorageOutcomeJson logStorageOutcome = fromStorageOutcome(storageOutcome);
230                        retVal.add(logStorageOutcome);
231                }
232
233                return retVal;
234        }
235
236        @Override
237        public TransactionLogStorageOutcomeJson clone() {
238                try {
239                        return (TransactionLogStorageOutcomeJson) super.clone();
240                } catch (CloneNotSupportedException e) {
241                        throw new UnsupportedOperationException();
242                }
243        }
244}