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