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 implements IModelJson, Comparable<TransactionLogStorageOutcomeJson> {
036        private static final Logger ourLog = LoggerFactory.getLogger(TransactionLogStorageOutcomeJson.class);
037
038        @JsonProperty("sourceUri")
039        private String mySourceUri;
040
041        @JsonProperty("sourceResourceType")
042        private String mySourceResourceType;
043
044        @JsonProperty("sourceResourceId")
045        private String mySourceResourceId;
046
047        @JsonProperty("targetResourceType")
048        private String myTargetResourceType;
049
050        @JsonProperty("targetResourceId")
051        private String myTargetResourceId;
052
053        @JsonProperty("targetResourceVersion")
054        private Long myTargetResourceVersion;
055
056        @JsonProperty("outcome")
057        private StorageResponseCodeEnum myOutcome;
058
059        @JsonProperty("errorMessage")
060        private String myErrorMessage;
061
062        public Long getTargetResourceVersion() {
063                return myTargetResourceVersion;
064        }
065
066        public TransactionLogStorageOutcomeJson setTargetResourceVersion(Long theTargetResourceVersion) {
067                myTargetResourceVersion = theTargetResourceVersion;
068                return this;
069        }
070
071        public String getErrorMessage() {
072                return myErrorMessage;
073        }
074
075        public TransactionLogStorageOutcomeJson setErrorMessage(String theErrorMessage) {
076                myErrorMessage = theErrorMessage;
077                return this;
078        }
079
080        public String getSourceUri() {
081                return mySourceUri;
082        }
083
084        public TransactionLogStorageOutcomeJson setSourceUri(String theSourceUri) {
085                mySourceUri = theSourceUri;
086                return this;
087        }
088
089        public String getTargetResourceType() {
090                return myTargetResourceType;
091        }
092
093        public TransactionLogStorageOutcomeJson setTargetResourceType(String theTargetResourceType) {
094                myTargetResourceType = theTargetResourceType;
095                return this;
096        }
097
098        public String getTargetResourceId() {
099                return myTargetResourceId;
100        }
101
102        public IIdType toTargetResourceIdType() {
103                String targetVersion = myTargetResourceVersion != null ? myTargetResourceVersion.toString() : null;
104                return new IdDt(myTargetResourceType, myTargetResourceId, targetVersion);
105        }
106
107        public TransactionLogStorageOutcomeJson setTargetResourceId(String theTargetResourceId) {
108                myTargetResourceId = theTargetResourceId;
109                return this;
110        }
111
112        public StorageResponseCodeEnum getOutcome() {
113                return myOutcome;
114        }
115
116        public TransactionLogStorageOutcomeJson setOutcome(StorageResponseCodeEnum theOutcome) {
117                myOutcome = theOutcome;
118                return this;
119        }
120
121        @Override
122        public int compareTo(@Nonnull TransactionLogStorageOutcomeJson o) {
123                return new CompareToBuilder()
124                                .append(mySourceUri, o.mySourceUri)
125                                .append(myTargetResourceType, o.myTargetResourceType)
126                                .append(myTargetResourceId, o.myTargetResourceId)
127                                .append(myOutcome, o.myOutcome)
128                                .append(myErrorMessage, o.myErrorMessage)
129                                .toComparison();
130        }
131
132        public String getSourceResourceType() {
133                return mySourceResourceType;
134        }
135
136        public TransactionLogStorageOutcomeJson setSourceResourceType(String theSourceResourceType) {
137                mySourceResourceType = theSourceResourceType;
138                return this;
139        }
140
141        public String getSourceResourceId() {
142                return mySourceResourceId;
143        }
144
145        public TransactionLogStorageOutcomeJson setSourceResourceId(String theSourceResourceId) {
146                mySourceResourceId = theSourceResourceId;
147                return this;
148        }
149
150        public IIdType toSourceResourceIdType() {
151                return new IdDt(mySourceResourceType, mySourceResourceId);
152        }
153
154        public static TransactionLogStorageOutcomeJson fromStorageOutcome(
155                        TransactionUtil.StorageOutcome theStorageOutcome) {
156
157                String sourceUri = theStorageOutcome.getRequestMetaSource();
158
159                boolean isFailure = theStorageOutcome.getStatusCode() >= 400 && theStorageOutcome.getStatusCode() <= 599;
160                String errorMessage = theStorageOutcome.getErrorMessage();
161                if (isBlank(errorMessage) && isFailure) {
162                        errorMessage = theStorageOutcome.getStatusMessage();
163                }
164
165                StorageResponseCodeEnum responseCode = theStorageOutcome.getStorageResponseCode();
166                IIdType sourceId = theStorageOutcome.getSourceId();
167                IIdType targetId = theStorageOutcome.getTargetId();
168
169                TransactionLogStorageOutcomeJson logStorageOutcome = new TransactionLogStorageOutcomeJson();
170                logStorageOutcome.setOutcome(responseCode);
171                logStorageOutcome.setErrorMessage(errorMessage);
172                if (targetId != null) {
173                        logStorageOutcome.setTargetResourceType(targetId.getResourceType());
174                        logStorageOutcome.setTargetResourceId(targetId.getIdPart());
175                        if (targetId.isVersionIdPartValidLong()) {
176                                logStorageOutcome.setTargetResourceVersion(targetId.getVersionIdPartAsLong());
177                        }
178                }
179                if (sourceId != null) {
180                        logStorageOutcome.setSourceResourceType(sourceId.getResourceType());
181                        logStorageOutcome.setSourceResourceId(sourceId.getIdPart());
182                }
183                logStorageOutcome.setSourceUri(sourceUri);
184                return logStorageOutcome;
185        }
186
187        public static List<TransactionLogStorageOutcomeJson> processResponseBundleForStorageOutcomes(
188                        @Nonnull FhirContext theFhirContext,
189                        @Nullable IBaseBundle theRequest,
190                        @Nullable IBaseBundle theResponse,
191                        String theGlobalErrorMessage) {
192                List<TransactionLogStorageOutcomeJson> retVal = new ArrayList<>();
193
194                FhirTerser terser = theFhirContext.newTerser();
195
196                if (isNotBlank(theGlobalErrorMessage)) {
197                        String requestBundleSource = terser.getSinglePrimitiveValueOrNull(theRequest, "Bundle.meta.source");
198                        List<IBase> requestEntries = terser.getValues(theRequest, "entry");
199                        int count = requestEntries.size();
200
201                        for (int i = 0; i < count; i++) {
202                                IBase requestEntry = requestEntries.get(i);
203                                IIdType targetId = terser.getSingleValue(requestEntry, "resource", IBaseResource.class)
204                                                .map(IBaseResource::getIdElement)
205                                                .orElse(null);
206                                if (targetId != null) {
207                                        TransactionLogStorageOutcomeJson storageOutcome = new TransactionLogStorageOutcomeJson();
208                                        storageOutcome.setSourceUri(requestBundleSource);
209                                        storageOutcome.setOutcome(StorageResponseCodeEnum.FAILURE);
210                                        storageOutcome.setErrorMessage(theGlobalErrorMessage);
211                                        storageOutcome.setTargetResourceType(targetId.getResourceType());
212                                        storageOutcome.setTargetResourceId(targetId.getIdPart());
213                                        retVal.add(storageOutcome);
214                                }
215                        }
216                        return retVal;
217                }
218
219                if (theResponse == null) {
220                        // This shouldn't happen
221                        ourLog.warn("No response Bundle provided but no error message was present");
222                        return retVal;
223                }
224
225                TransactionUtil.TransactionResponse transactionResponse =
226                                TransactionUtil.parseTransactionResponse(theFhirContext, theRequest, theResponse);
227                for (TransactionUtil.StorageOutcome storageOutcome : transactionResponse.getStorageOutcomes()) {
228                        TransactionLogStorageOutcomeJson logStorageOutcome = fromStorageOutcome(storageOutcome);
229                        retVal.add(logStorageOutcome);
230                }
231
232                return retVal;
233        }
234}