001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2025 Smile CDR, Inc. 006 * %% 007 * All rights reserved. 008 * #L% 009 */ 010package ca.cdr.api.pub.hl7v2.model; 011 012import ca.cdr.api.model.json.IModelJson; 013import ca.uhn.hl7v2.HL7Exception; 014import ca.uhn.hl7v2.model.AbstractMessage; 015import ca.uhn.hl7v2.model.Message; 016import com.fasterxml.jackson.annotation.JsonCreator; 017import com.fasterxml.jackson.annotation.JsonProperty; 018import com.fasterxml.jackson.annotation.JsonPropertyOrder; 019import io.swagger.v3.oas.annotations.media.Schema; 020import jakarta.annotation.Nonnull; 021import org.hl7.fhir.instance.model.api.IBaseBundle; 022import org.springframework.util.CollectionUtils; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.BUNDLES; 030import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.DO_AUTO_CONVERT; 031import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.DO_PROCESS; 032import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.ISSUES; 033import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.MODIFIABLE_MESSAGE; 034import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.ORIGINAL_MESSAGE; 035import static ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson.REQUEST_HEADERS; 036import static java.util.Objects.isNull; 037 038/** 039 * Contains all relevant data involved in the conversion of an HL7 v2.x message to a list of IBaseBundle resources. 040 */ 041@JsonPropertyOrder({ORIGINAL_MESSAGE, MODIFIABLE_MESSAGE, BUNDLES, ISSUES, DO_PROCESS, DO_AUTO_CONVERT, REQUEST_HEADERS 042}) 043public class Hl7v2ToFhirConversionResultJson implements IModelJson { 044 045 public static final String ORIGINAL_MESSAGE = "originalMessage"; 046 public static final String MODIFIABLE_MESSAGE = "modifiableMessage"; 047 public static final String BUNDLES = "bundles"; 048 public static final String ISSUES = "issues"; 049 public static final String DO_PROCESS = "doProcess"; 050 public static final String DO_AUTO_CONVERT = "doAutoConvert"; 051 public static final String REQUEST_HEADERS = "requestHeaders"; 052 053 /** 054 * The original HL7 v2.x message before any customizations have been applied. 055 */ 056 @JsonProperty(ORIGINAL_MESSAGE) 057 private Message myOriginalMessage; 058 059 /** 060 * The HL7 v2.x message which customizations can be applied to. 061 */ 062 @JsonProperty(MODIFIABLE_MESSAGE) 063 private Message myModifiableMessage; 064 065 /** 066 * A list of Bundle resources that have been created from the modifiableMessage. 067 */ 068 @JsonProperty(BUNDLES) 069 private List<IBaseBundle> myBundles = new ArrayList<>(); 070 071 /** 072 * A list of issues that have occurred throughout the conversion process. 073 */ 074 @JsonProperty(ISSUES) 075 private List<MappingMessage> myIssues = new ArrayList<>(); 076 077 /** 078 * A flag to indicate whether a given message should be processed. 079 */ 080 @JsonProperty(DO_PROCESS) 081 private boolean myDoProcess; 082 083 /** 084 * A flag to indicate whether a given message should be passed through the Smile generic mapper. 085 * Set this to false in order to skip the Smile generic mapper entirely. 086 */ 087 @JsonProperty(DO_AUTO_CONVERT) 088 private boolean myDoAutoConvert; 089 090 @JsonProperty(REQUEST_HEADERS) 091 @Schema(description = "The HTTP request headers associated with the message transmission when applicable") 092 private Map<String, String> myRequestHeaders; 093 094 /** 095 * Constructor 096 * @param theOriginalMessage The original message to be preserved during the HL7V2 to FHIR conversion 097 */ 098 @JsonCreator 099 public Hl7v2ToFhirConversionResultJson(@JsonProperty(ORIGINAL_MESSAGE) Message theOriginalMessage) 100 throws HL7Exception { 101 myOriginalMessage = theOriginalMessage; 102 myModifiableMessage = cloneMessage(theOriginalMessage); 103 myDoProcess = true; 104 myDoAutoConvert = true; 105 } 106 107 /** 108 * @deprecated Use {@link AbstractMessage#copy()} instead. 109 * <br/><br/> 110 * Clones the specified {@link Message} into a new {@link Message} with the same contents 111 * @param theMessage The message to clone 112 */ 113 @Deprecated(since = "2023.11.R01") 114 public static Message cloneMessage(Message theMessage) throws HL7Exception { 115 return ((AbstractMessage) theMessage).copy(); 116 } 117 118 /** 119 * Adds a bundle 120 * @param theBundle The bundle to add 121 */ 122 public void addBundle(IBaseBundle theBundle) { 123 myBundles.add(theBundle); 124 } 125 126 /** 127 * Adds a message to the conversion result. Acceptable message levels are `INFO`, `WARNING`, and `ERROR` 128 * 129 * @param thePath The path within the message where the issue was detected 130 * @param theMessageLevel The issue error level, e.g. 'INFO', 'WARNING', or 'ERROR'. 131 * @param theIssue The description of the issue 132 */ 133 public void addIssue(String thePath, MappingMessage.MessageLevel theMessageLevel, String theIssue) { 134 MappingMessage issue = new MappingMessage(thePath, MappingMessage.PathType.HL7V2, theMessageLevel, theIssue); 135 this.addIssue(issue); 136 } 137 138 /** 139 * Adds an issue 140 * @param theIssue The issue to add 141 */ 142 public void addIssue(MappingMessage theIssue) { 143 if (myIssues.isEmpty()) { 144 myIssues = new ArrayList<>(); 145 } 146 myIssues.add(theIssue); 147 } 148 149 /** 150 * Indicates whether there were any issues ({@link MappingMessage.MessageLevel#INFO}, 151 * {@link MappingMessage.MessageLevel#WARNING}, or {@link MappingMessage.MessageLevel#ERROR}) 152 * during the message conversion 153 * @return true if there are any issues 154 */ 155 public boolean hasIssues() { 156 return !CollectionUtils.isEmpty(myIssues); 157 } 158 159 /** 160 * Indicates whether there were any {@link MappingMessage.MessageLevel#INFO} issues 161 * during the message conversion 162 * @return true if there are any {@link MappingMessage.MessageLevel#INFO} issues 163 */ 164 public boolean hasInfoIssues() { 165 return hasIssueOfType(MappingMessage.MessageLevel.INFO); 166 } 167 168 /** 169 * Indicates whether there were any {@link MappingMessage.MessageLevel#WARNING} issues 170 * during the message conversion 171 * @return true if there are any {@link MappingMessage.MessageLevel#WARNING} issues 172 */ 173 public boolean hasWarningIssues() { 174 return hasIssueOfType(MappingMessage.MessageLevel.WARNING); 175 } 176 177 /** 178 * Indicates whether there were any {@link MappingMessage.MessageLevel#ERROR} issues 179 * during the message conversion 180 * @return true if there are any {@link MappingMessage.MessageLevel#ERROR} issues 181 */ 182 public boolean hasErrorIssues() { 183 return hasIssueOfType(MappingMessage.MessageLevel.ERROR); 184 } 185 186 private boolean hasIssueOfType(MappingMessage.MessageLevel theType) { 187 return myIssues.stream().anyMatch(m -> theType.equals(m.getLevel())); 188 } 189 190 /** 191 * @return A copy of the original HL7 v2.x message before any customizations have been applied. 192 */ 193 public Message getOriginalMessage() throws HL7Exception { 194 return cloneMessage(myOriginalMessage); 195 } 196 197 /** 198 * @return The HL7 v2.x message which customizations can be applied to. 199 */ 200 public Message getModifiableMessage() { 201 return myModifiableMessage; 202 } 203 204 /** 205 * Sets the HL7 v2.x message which customizations can be applied to. 206 * @param theModifiableMessage The modifiable Message 207 */ 208 public void setModifiableMessage(Message theModifiableMessage) { 209 myModifiableMessage = theModifiableMessage; 210 } 211 212 /** 213 * @return A list of Bundle resources that have been created from the modifiable Message. 214 */ 215 public List<IBaseBundle> getBundles() { 216 return myBundles; 217 } 218 219 /** 220 * Sets the list of Bundle resources that have been created from the modifiable Message. 221 * @param theBundles The Bundles 222 */ 223 public void setBundles(List<IBaseBundle> theBundles) { 224 myBundles = theBundles; 225 } 226 227 /** 228 * @return A list of issues that have occurred throughout the conversion process. 229 */ 230 public List<MappingMessage> getIssues() { 231 return myIssues; 232 } 233 234 /** 235 * Sets the list of issues that have occurred throughout the conversion process. 236 * @param theIssues The issues 237 */ 238 public void setIssues(List<MappingMessage> theIssues) { 239 myIssues = new ArrayList<>(); 240 addIssues(theIssues); 241 } 242 243 /** 244 * Adds the items in list of issues that have occurred throughout the conversion process. 245 * @param theIssues The issues 246 */ 247 public void addIssues(List<MappingMessage> theIssues) { 248 if (!theIssues.isEmpty()) { 249 myIssues.addAll(theIssues); 250 } 251 } 252 253 /** 254 * @return Whether the modifiable Message should be processed. 255 */ 256 public boolean isDoProcess() { 257 return myDoProcess; 258 } 259 260 /** 261 * Sets a flag to indicate whether the modifiable Message should be processed. 262 * @param theDoProcess The value of the flag 263 */ 264 public void setDoProcess(boolean theDoProcess) { 265 myDoProcess = theDoProcess; 266 } 267 268 /** 269 * @return Whether the modifiable Message should be passed through the Smile generic mapper. 270 */ 271 public boolean isDoAutoConvert() { 272 return myDoAutoConvert; 273 } 274 275 /** 276 * Sets a flag to indicate whether the modifiable Message should be passed through the Smile generic mapper. 277 * <br/> 278 * The Smile generic mapper will be skipped entirely if this flag is set to false. 279 * @param theDoAutoConvert The value of the flag 280 */ 281 public void setDoAutoConvert(boolean theDoAutoConvert) { 282 myDoAutoConvert = theDoAutoConvert; 283 } 284 285 /** 286 * 287 * @return the http request headers associated with the request (if any). 288 */ 289 @Nonnull 290 public Map<String, String> getRequestHeaders() { 291 if (isNull(myRequestHeaders)) { 292 myRequestHeaders = new HashMap<>(); 293 } 294 295 return myRequestHeaders; 296 } 297 298 /** 299 * Stores the request headers associated with the request (if any) 300 * @param theRequestHeaders 301 * @return 302 */ 303 public void setRequestHeaders(Map<String, String> theRequestHeaders) { 304 myRequestHeaders = theRequestHeaders; 305 } 306}