diff --git a/build.gradle b/build.gradle index 1d233bc1d0..d5a428ac92 100644 --- a/build.gradle +++ b/build.gradle @@ -162,6 +162,10 @@ tasks.withType(Test) { dependencies { + // Enables serialisation of java.util.Optional and java.time.LocalDateTime + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' + implementation('org.springframework.cloud:spring-cloud-starter-bootstrap') { version { strictly '4.0.5' @@ -662,7 +666,7 @@ sonarqube { properties { property "sonar.exclusions", "build/generated-sources/**/*.java," + "**/AppInsightsConfiguration.java," + - "**/TestingSupportController.java" + "**/TestingSupportController.java,**/JcLogger.java,**/CaseDataContent.java,**/CaseDetails.java,**/MidEventCallback.java" property "sonar.projectName", "ccd-data-store-api" property "sonar.projectKey", "ccd-data-store-api" property "sonar.coverage.jacoco.xmlReportPaths", "${jacocoTestReport.reports.xml.outputLocation}" diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseDetails.java b/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseDetails.java index 108c3e0e06..1096463d0d 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseDetails.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseDetails.java @@ -12,6 +12,7 @@ import org.springframework.http.ResponseEntity; import uk.gov.hmcts.ccd.data.casedetails.SecurityClassification; import uk.gov.hmcts.ccd.domain.model.callbacks.AfterSubmitCallbackResponse; +import uk.gov.hmcts.ccd.domain.service.common.JcLogger; import java.time.LocalDate; import java.time.LocalDateTime; @@ -42,6 +43,8 @@ public class CaseDetails implements Cloneable { private static final Logger LOG = LoggerFactory.getLogger(CaseDetails.class); public static final String DRAFT_ID = "DRAFT%s"; + final JcLogger jcLogger = new JcLogger("CCD-6087", "CaseDetails", true); + private String id; @JsonIgnore @@ -202,7 +205,16 @@ public Map getData() { return data; } + private void logData(final Map data, final String methodName) { + final String json = jcLogger.getObjectAsString(data); + if (json.contains("dummy.pdf") || json.contains("JSON ERROR")) { + jcLogger.jclog(methodName + " json = " + json); + jcLogger.jclog(methodName + " CALL STACK = " + JcLogger.getStackTraceAsString(new Exception())); + } + } + public void setData(Map data) { + logData(data, "CaseDetails.setData()"); this.data = data; } @@ -235,6 +247,7 @@ public Map getSupplementaryData() { } public void setSupplementaryData(Map supplementaryData) { + logData(data, "CaseDetails.setSupplementaryData()"); this.supplementaryData = supplementaryData; } @@ -363,6 +376,7 @@ public boolean hasCaseReference() { public Map getCaseEventData(Set caseFieldIds) { Map caseEventData = new HashMap<>(); if (this.data != null) { + logData(data, "CaseDetails.getCaseEventData()"); for (String caseFieldId : caseFieldIds) { JsonNode value = this.data.get(caseFieldId); if (value != null) { diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/model/std/CaseDataContent.java b/src/main/java/uk/gov/hmcts/ccd/domain/model/std/CaseDataContent.java index b4b3bbaff9..a0390554cc 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/model/std/CaseDataContent.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/model/std/CaseDataContent.java @@ -6,9 +6,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import lombok.ToString; +import uk.gov.hmcts.ccd.domain.service.common.JcLogger; @ToString public class CaseDataContent { + final JcLogger jcLogger = new JcLogger("CCD-6087", "CaseDataContent", true); + private Event event; private Map data; @@ -60,7 +63,16 @@ public Map getData() { return data; } + private void logData(final Map data, final String methodName) { + final String json = jcLogger.getObjectAsString(data); + if (json.contains("dummy.pdf") || json.contains("JSON ERROR")) { + jcLogger.jclog(methodName + " json = " + json); + jcLogger.jclog(methodName + " CALL STACK = " + JcLogger.getStackTraceAsString(new Exception())); + } + } + public void setData(Map data) { + logData(data, "CaseDataContent.setData()"); this.data = data; } @@ -69,6 +81,7 @@ public Map getEventData() { } public void setEventData(Map eventData) { + logData(eventData, "CaseDataContent.setEventData()"); this.eventData = eventData; } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/callbacks/CallbackService.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/callbacks/CallbackService.java index 4da40f3ab8..c4cc683922 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/callbacks/CallbackService.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/callbacks/CallbackService.java @@ -26,6 +26,7 @@ import uk.gov.hmcts.ccd.domain.model.callbacks.CallbackResponse; import uk.gov.hmcts.ccd.domain.model.definition.CaseDetails; import uk.gov.hmcts.ccd.domain.model.definition.CaseEventDefinition; +import uk.gov.hmcts.ccd.domain.service.common.JcLogger; import uk.gov.hmcts.ccd.endpoint.exceptions.ApiException; import uk.gov.hmcts.ccd.endpoint.exceptions.CallbackException; import uk.gov.hmcts.ccd.util.ClientContextUtil; @@ -47,6 +48,8 @@ public class CallbackService { private static final String WILDCARD = "*"; public static final String CLIENT_CONTEXT = "Client-Context"; + final JcLogger jcLogger = new JcLogger("CCD-6087", "CallbackService", false); + private final SecurityUtils securityUtils; private final RestTemplate restTemplate; private final ApplicationParams applicationParams; @@ -130,6 +133,11 @@ public ResponseEntity sendSingleRequest(final String url, }); } + /* + * JC note: + * CallbackRequest DOES contain "dummy.pdf" , for both "Continue 1" (with attachment) and "Continue 2" (without). + * Called from CaseDataValidatorController. + */ private Optional> sendRequest(final String url, final CallbackType callbackType, final Class clazz, @@ -150,6 +158,9 @@ private Optional> sendRequest(final String url, } final HttpEntity requestEntity = new HttpEntity(callbackRequest, httpHeaders); if (logCallbackDetails(url)) { + jcLogger.jclog("sendRequest() url = " + url + " , callbackType = " + callbackType); + jcLogger.jclog("sendRequest() callbackRequest", callbackRequest); + jcLogger.jclog("sendRequest() CALL STACK = " + JcLogger.getStackTraceAsString(new Exception())); LOG.info("Invoking callback {} of type {} with request: {}", url, callbackType, printCallbackDetails(requestEntity)); } @@ -182,7 +193,6 @@ private String printCallbackDetails(HttpEntity callbackHttpEntity) { } catch (Exception ex) { LOG.warn("Unexpected error while logging callback: {}", ex.getMessage()); } - return null; } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/common/CaseService.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/CaseService.java index 9dd2ee92a8..e7422112b5 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/common/CaseService.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/CaseService.java @@ -30,6 +30,8 @@ // partal javadoc attributes added prior to checkstyle implementation in module public class CaseService { + final JcLogger jcLogger = new JcLogger("CCD-6087", "CaseService", true); + private final CaseDataService caseDataService; private final CaseDetailsRepository caseDetailsRepository; private final UIDService uidService; @@ -74,7 +76,22 @@ public CaseDetails createNewCaseDetails(String caseTypeId, String jurisdictionId * @return Optional<CaseDetails> - CaseDetails wrapped in Optional */ public CaseDetails populateCurrentCaseDetailsWithEventFields(CaseDataContent content, CaseDetails caseDetails) { - content.getEventData().forEach((key, value) -> caseDetails.getData().put(key, value)); + final Map eventData = content.getEventData(); + Map caseData = caseDetails.getData(); + + // LOG eventData + eventData.forEach((key, value) -> jcLogger.jclog("eventData: " + key + " = " + value)); + + // LOG caseDataBefore + caseData.forEach((key, value) -> jcLogger.jclog("caseDataBefore: " + key + " = " + value)); + + // Process eventData -> caseData + eventData.forEach((key, value) -> caseData.put(key, value)); + caseDetails.setData(caseData); + + // LOG caseDataAfter + caseData.forEach((key, value) -> jcLogger.jclog("caseDataAfter: " + key + " = " + value)); + return caseDetails; } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/common/JcLogger.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/JcLogger.java new file mode 100644 index 0000000000..3a11dbad11 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/JcLogger.java @@ -0,0 +1,134 @@ +package uk.gov.hmcts.ccd.domain.service.common; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import uk.gov.hmcts.ccd.data.casedetails.SecurityClassification; +import uk.gov.hmcts.ccd.domain.model.callbacks.CallbackRequest; +import uk.gov.hmcts.ccd.domain.model.callbacks.StartEventResult; +import uk.gov.hmcts.ccd.domain.model.definition.CaseDetails; +import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Map; +import java.util.Optional; + +public class JcLogger { + + private static final Logger LOG = LoggerFactory.getLogger(JcLogger.class); + + private final String jiraRef; + + private final String classname; + + private final boolean enabled; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public JcLogger(final String jiraRef, final String classname, final boolean enabled) { + this.jiraRef = jiraRef; + this.classname = classname; + this.enabled = enabled; + // Enables serialisation of java.util.Optional and java.time.LocalDateTime + objectMapper.registerModule(new Jdk8Module()); + objectMapper.registerModule(new JavaTimeModule()); + } + + public void jclog(String message) { + if (enabled) { + LOG.info("| JCDEBUG {}: {}: {}", jiraRef, classname, message); + } + } + + public void jclog(String message, int i) { + jclog(message + ": " + i); + } + + public void jclog(String message, CaseDataContent caseDataContent) { + try { + jclog(message + ": " + objectMapper.writeValueAsString(caseDataContent)); + } catch (JsonProcessingException e) { + jclog(message + ": JSON ERROR: " + e.getMessage()); + } + } + + public void jclog(String message, CallbackRequest callbackRequest) { + try { + jclog(message + ": " + objectMapper.writeValueAsString(callbackRequest)); + } catch (JsonProcessingException e) { + jclog(message + ": JSON ERROR: " + e.getMessage()); + } + } + + public void jclog(String message, Optional optional) { + try { + jclog(message + ": " + objectMapper.writeValueAsString(optional)); + } catch (JsonProcessingException e) { + jclog(message + ": JSON ERROR: " + e.getMessage()); + } + } + + public void jclog(String message, CaseDetails caseDetails) { + try { + jclog(message + ": " + objectMapper.writeValueAsString(caseDetails)); + } catch (JsonProcessingException e) { + jclog(message + ": JSON ERROR: " + e.getMessage()); + } + } + + public void jclog(String message, SecurityClassification securityClassification) { + try { + jclog(message + ": " + objectMapper.writeValueAsString(securityClassification)); + } catch (JsonProcessingException e) { + jclog(message + ": JSON ERROR: " + e.getMessage()); + } + } + + public void jclog(String message, StartEventResult startEventResult) { + try { + jclog(message + ": " + objectMapper.writeValueAsString(startEventResult)); + } catch (JsonProcessingException e) { + jclog(message + ": JSON ERROR: " + e.getMessage()); + } + } + + public String getObjectAsString(final Map value) { + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + jclog("JSON ERROR: " + e.getMessage()); + return "JSON ERROR: " + e.getMessage(); + } + } + + public String getObjectAsString(final CaseDataContent caseDataContent) { + try { + return objectMapper.writeValueAsString(caseDataContent); + } catch (JsonProcessingException e) { + jclog("JSON ERROR: " + e.getMessage()); + return "JSON ERROR: " + e.getMessage(); + } + } + + public String getObjectAsString(final CaseDetails caseDetails) { + try { + return objectMapper.writeValueAsString(caseDetails); + } catch (JsonProcessingException e) { + jclog("JSON ERROR: " + e.getMessage()); + return "JSON ERROR: " + e.getMessage(); + } + } + + public static String getStackTraceAsString(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + throwable.printStackTrace(pw); + String stackTrace = sw.toString().replaceAll("\r\n", " ").replaceAll("\n", " "); + return stackTrace.hashCode() + " " + stackTrace; + } +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallback.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallback.java index 56fbbdc148..82aba92db8 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallback.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallback.java @@ -23,6 +23,7 @@ import uk.gov.hmcts.ccd.domain.model.std.Event; import uk.gov.hmcts.ccd.domain.service.common.CaseService; import uk.gov.hmcts.ccd.domain.service.common.EventTriggerService; +import uk.gov.hmcts.ccd.domain.service.common.JcLogger; import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker; import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; @@ -37,6 +38,8 @@ public class MidEventCallback { private final CaseDefinitionRepository caseDefinitionRepository; private final CaseService caseService; + final JcLogger jcLogger = new JcLogger("CCD-6087", "MidEventCallback", true); + @Autowired public MidEventCallback(CallbackInvoker callbackInvoker, UIDefinitionRepository uiDefinitionRepository, @@ -51,11 +54,38 @@ public MidEventCallback(CallbackInvoker callbackInvoker, this.caseService = caseService; } + private void logData(final CaseDataContent caseDataContent, final String methodReference) { + final String json = jcLogger.getObjectAsString(caseDataContent); + if (json.contains("dummy.pdf")) { + jcLogger.jclog(methodReference + ": YES , json = " + json.hashCode() + " " + json); + } else { + jcLogger.jclog(methodReference + ": NO , json = " + json.hashCode() + " " + json); + } + } + + private void logData(final CaseDetails caseDetails, final String methodReference) { + final String json = jcLogger.getObjectAsString(caseDetails); + if (json.contains("dummy.pdf")) { + jcLogger.jclog(methodReference + ": YES , json = " + json.hashCode() + " " + json); + } else { + jcLogger.jclog(methodReference + ": NO , json = " + json.hashCode() + " " + json); + } + } + + /* + * Called directly from CaseDataValidatorController.validate() + * Method below references 'caseDetailsBefore' , 'currentOrNewCaseDetails' , 'caseDetails' , and + * 'caseDetailsFromMidEventCallback' + */ @Transactional public JsonNode invoke(String caseTypeId, CaseDataContent content, String pageId) { if (!isBlank(pageId)) { + + // QUESTION: Does 'CaseDataContent content' contain reference to "dummy.pdf" ? + logData(content, "invoke #1"); + Event event = content.getEvent(); final CaseTypeDefinition caseTypeDefinition = getCaseType(caseTypeId); final CaseEventDefinition caseEventDefinition = getCaseEvent(event, caseTypeDefinition); @@ -71,11 +101,27 @@ public JsonNode invoke(String caseTypeId, CaseDetails caseDetailsBefore = null; CaseDetails currentOrNewCaseDetails; if (StringUtils.isNotEmpty(content.getCaseReference())) { - CaseDetails caseDetails = + + /* + * LINE 75 (call caseService.getCaseDetails()) + */ + final CaseDetails caseDetails = caseService.getCaseDetails(caseTypeDefinition.getJurisdictionId(), content.getCaseReference()); + + // QUESTION: Does caseDetails contain reference to "dummy.pdf" ? + logData(caseDetails, "invoke #2"); + caseDetailsBefore = caseService.clone(caseDetails); + + // QUESTION: Does caseDetailsBefore contain reference to "dummy.pdf" ? + logData(caseDetailsBefore, "invoke #3"); + currentOrNewCaseDetails = caseService.populateCurrentCaseDetailsWithEventFields(content, caseDetails); + + // QUESTION: Does currentOrNewCaseDetails contain reference to "dummy.pdf" ? + logData(currentOrNewCaseDetails, "invoke #4"); + } else { currentOrNewCaseDetails = caseService.createNewCaseDetails(caseTypeId, caseTypeDefinition.getJurisdictionId(), @@ -84,10 +130,15 @@ public JsonNode invoke(String caseTypeId, removeNextPageFieldData(currentOrNewCaseDetails, wizardPageOptional.get().getOrder(), caseTypeId, event.getEventId()); + // QUESTION: Does currentOrNewCaseDetails contain reference to "dummy.pdf" ? + logData(currentOrNewCaseDetails, "invoke #5"); + + /* + * LINE 88 (call callbackInvoker.invokeMidEventCallback()) + */ CaseDetails caseDetailsFromMidEventCallback = callbackInvoker.invokeMidEventCallback(wizardPageOptional.get(), - caseTypeDefinition, - caseEventDefinition, + caseTypeDefinition, caseEventDefinition, caseDetailsBefore, currentOrNewCaseDetails, content.getIgnoreWarning()); diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/processor/FieldProcessor.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/processor/FieldProcessor.java index 30f4a26f4c..e81ec3f8b5 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/processor/FieldProcessor.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/processor/FieldProcessor.java @@ -35,6 +35,7 @@ public JsonNode execute(JsonNode node, WizardPageField wizardPageField) { CaseViewField caseViewField = caseViewFieldBuilder.build(caseField, caseEventField); + // TODO: Add logging here if (caseViewField.isComplexFieldType()) { return executeComplex(node, caseField.getFieldTypeDefinition().getComplexFields(), wizardPageField, caseField.getId(), caseViewField); diff --git a/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorController.java b/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorController.java index 3e1da513f4..ae3a6acc9a 100644 --- a/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorController.java +++ b/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorController.java @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController; import uk.gov.hmcts.ccd.config.JacksonUtils; import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; +import uk.gov.hmcts.ccd.domain.service.common.JcLogger; import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.v2.V2; @@ -29,6 +30,8 @@ public class CaseDataValidatorController { private final ValidateCaseFieldsOperation validateCaseFieldsOperation; private final MidEventCallback midEventCallback; + final JcLogger jcLogger = new JcLogger("CCD-6087", "CaseDataValidatorController", true); + @Autowired public CaseDataValidatorController( ValidateCaseFieldsOperation validateCaseFieldsOperation, @@ -71,9 +74,22 @@ public CaseDataValidatorController( public ResponseEntity validate(@PathVariable("caseTypeId") String caseTypeId, @RequestParam(required = false) final String pageId, @RequestBody final CaseDataContent content) { - validateCaseFieldsOperation.validateCaseDetails(caseTypeId, - content); + /* + * JC note: + * CaseDataContent DOES contain "dummy.pdf" , caseTypeId = "FinancialRemedyMVP2" , pageId = "FR_generalEmail1" + * POST path = "/case-types/FinancialRemedyMVP2/validate" + */ + jcLogger.jclog("validate() caseTypeId = " + caseTypeId + " , pageId = " + pageId + " , [built 12th Feb]"); + jcLogger.jclog("validate() caseDataContent", content); + + validateCaseFieldsOperation.validateCaseDetails(caseTypeId, content); + /* + * CCD-6087 ("Mid-event callback request contains unexpected data") + * CCD-6087 calls invoke() which in turn calls CaseService.populateCurrentCaseDetailsWithEventFields() + * ^^^^^^^^^^^ + * CCD-6087 -- see "CaseService" , "caseDataAfter" + */ final JsonNode data = midEventCallback.invoke(caseTypeId, content, pageId);