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.util; 011 012import ca.cdr.api.model.json.TransactionLogStepJson; 013import ca.cdr.api.pub.hl7v2.model.MappingMessage; 014import ca.uhn.fhir.rest.api.server.RequestDetails; 015import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; 016import com.fasterxml.jackson.core.JsonProcessingException; 017import com.fasterxml.jackson.databind.ObjectMapper; 018import org.apache.commons.lang3.Validate; 019 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026/** 027 * NOTE: this utility is using vanilla Jackson - FHIR resources can not be serialized. 028 */ 029public class TransactionLogRequestDetailsUtil { 030 public static final String STEPS_LIST_KEY = TransactionLogRequestDetailsUtil.class.getName() + "_STEPS_LIST"; 031 public static final String REQUEST_SUBTYPE_KEY = 032 TransactionLogRequestDetailsUtil.class.getName() + "_REQUEST_SUBTYPE_KEY"; 033 private static final String MESSAGES_LIST_KEY = TransactionLogRequestDetailsUtil.class.getName() + "_MESSAGES_LIST"; 034 private static final String ADDITIONAL_JSON_MAP_KEY = 035 TransactionLogRequestDetailsUtil.class.getName() + "_ADDITIONAL_JSON_MAP"; 036 037 private static final ObjectMapper ourObjectMapper = new ObjectMapper(); 038 039 private TransactionLogRequestDetailsUtil() {} 040 041 public static void addMessageToRequest(RequestDetails theDetails, MappingMessage theMessage) { 042 Validate.notNull(theDetails, "theDetails must not be null"); 043 Validate.notNull(theMessage, "theMessage must not be null"); 044 @SuppressWarnings("unchecked") 045 List<MappingMessage> messages = 046 (List<MappingMessage>) theDetails.getUserData().get(MESSAGES_LIST_KEY); 047 if (messages == null) { 048 messages = new ArrayList<>(); 049 theDetails.getUserData().put(MESSAGES_LIST_KEY, messages); 050 } 051 messages.add(theMessage); 052 } 053 054 public static List<MappingMessage> getMessagesOnRequest(RequestDetails theDetails) { 055 @SuppressWarnings("unchecked") 056 List<MappingMessage> messages = 057 (List<MappingMessage>) theDetails.getUserData().get(MESSAGES_LIST_KEY); 058 if (messages == null) { 059 return List.of(); 060 } 061 return Collections.unmodifiableList(messages); 062 } 063 064 public static List<TransactionLogStepJson> getTransactionLogStepsFromRequest(RequestDetails theRequestDetails) { 065 @SuppressWarnings("unchecked") 066 List<TransactionLogStepJson> steps = 067 (List<TransactionLogStepJson>) theRequestDetails.getUserData().get(STEPS_LIST_KEY); 068 if (steps == null) { 069 return List.of(); 070 } 071 return Collections.unmodifiableList(steps); 072 } 073 074 /** 075 * Adds a transaction log step to the attribute which holds the list of steps for the provided request. 076 * @param theRequest the request 077 * @param theStep the transaction log step to add 078 */ 079 public static void addTransactionLogStepForRequest(RequestDetails theRequest, TransactionLogStepJson theStep) { 080 Validate.notNull(theRequest, "theRequest must not be null"); 081 Validate.notNull(theStep, "theStep must not be null"); 082 @SuppressWarnings("unchecked") 083 List<TransactionLogStepJson> steps = 084 (List<TransactionLogStepJson>) theRequest.getUserData().get(STEPS_LIST_KEY); 085 if (steps == null) { 086 steps = new ArrayList<>(); 087 theRequest.getUserData().put(STEPS_LIST_KEY, steps); 088 } 089 steps.add(theStep); 090 } 091 092 /** 093 * NOTE: this method is using vanilla Jackson - FHIR resources can not be serialized. 094 * 095 * Utility method to add key/value pairs to a transactionLog. The provided value needs to be serializable 096 * with Jackson since pairs are accumulated and subsequently formatted in an inline json string. 097 * IllegalArgumentException is thrown if <code>theValue</code> is not Json serializable with Jackson or <code>null</code>. 098 * The additional log properties will be tallied and rendered as inline json in the transactionLogEventJson: 099 *{ 100 * "id": 2, 101 * "initialTimestamp": "2023-08-29T14:00:30.096-04:00", 102 * "type": "FHIR_REQUEST", 103 * "subType": "FHIR_CREATE", 104 * "outcome": "SUCCESS", 105 * ... 106 * "requestId": "r1bW96uhRAn2z8iU", 107 * "additionalJson": { "diseases": [ "Tuberculosis", "Polio", "Malaria", "Dengue fever"], "clinic": "Acme Ouest" }, 108 * ... 109 * "userFamilyName": "GenericUser", 110 * "userGivenName": "Admin" 111 * } 112 * 113 * @param theRequestDetails The request details where the pairs will be stored 114 * @param theKey The key for referencing to theObject. Keys are transformed into json properties when serializing a pair. 115 * @param theValue The value needing storage. Values are transformed into json values when serializing a pair. 116 * 117 */ 118 public static void addAdditionalJsonProperty(RequestDetails theRequestDetails, String theKey, Object theValue) { 119 Validate.notNull(theRequestDetails, "theDetails must not be null"); 120 Validate.notNull(theKey, "theKey must not be null"); 121 Validate.notNull(theValue, "theValue must not be null"); 122 123 try { 124 // ensure that the value can be serialized to JSON 125 ourObjectMapper.writeValueAsString(theValue); 126 } catch (JsonProcessingException e) { 127 throw new IllegalArgumentException( 128 String.format( 129 "Unable to add pair with key %s to the additional Json property as the provided value object could not be parsed.", 130 theKey), 131 e); 132 } 133 @SuppressWarnings("unchecked") 134 Map<String, Object> additionalJsonPropertiesMap = 135 (Map<String, Object>) theRequestDetails.getUserData().get(ADDITIONAL_JSON_MAP_KEY); 136 if (additionalJsonPropertiesMap == null) { 137 additionalJsonPropertiesMap = new HashMap<>(); 138 theRequestDetails.getUserData().put(ADDITIONAL_JSON_MAP_KEY, additionalJsonPropertiesMap); 139 } 140 additionalJsonPropertiesMap.put(theKey, theValue); 141 } 142 143 public static Map<String, Object> getAdditionalJsonPropertiesMap(RequestDetails theRequestDetails) { 144 Validate.notNull(theRequestDetails, "theRequestDetails must not be null"); 145 @SuppressWarnings("unchecked") 146 Map<String, Object> additionalJsonPropertiesMap = 147 (Map<String, Object>) theRequestDetails.getUserData().get(ADDITIONAL_JSON_MAP_KEY); 148 if (additionalJsonPropertiesMap == null) { 149 return Map.of(); 150 } 151 return additionalJsonPropertiesMap; 152 } 153 154 public static boolean hasTransactionLogSteps(ServletRequestDetails theRequestDetails) { 155 return theRequestDetails.getUserData().containsKey(STEPS_LIST_KEY); 156 } 157}