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}