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