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}