From 4c152561319dda66c42fd90994f91cf01f301204 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Tue, 9 Apr 2024 16:25:56 +0100 Subject: [PATCH 1/5] implement apply access control to /validate endpoints --- .../ContractTestCreateCaseOperation.java | 7 +- .../uk/gov/hmcts/ccd/config/JacksonUtils.java | 10 ++ .../DefaultCreateCaseOperation.java | 9 +- .../createevent/CreateCaseEventService.java | 2 + ...AuthorisedValidateCaseFieldsOperation.java | 105 ++++++++++++++++++ .../DefaultValidateCaseFieldsOperation.java | 6 +- .../service/validate/OperationContext.java | 9 ++ .../validate/ValidateCaseFieldsOperation.java | 4 +- .../ccd/endpoint/std/CaseDetailsEndpoint.java | 16 +-- .../CaseDataValidatorController.java | 23 +--- .../external/resource/CaseDataResource.java | 2 +- .../DefaultCreateCaseOperationTest.java | 36 +++--- ...efaultValidateCaseFieldsOperationTest.java | 45 +++++--- .../endpoint/std/CaseDetailsEndpointIT.java | 33 ++++-- .../endpoint/std/CaseDetailsEndpointTest.java | 63 +++++------ .../uk/gov/hmcts/ccd/v2/DCPTestHelper.java | 56 +++++----- .../CaseDataValidatorControllerTest.java | 8 +- 17 files changed, 286 insertions(+), 148 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/validate/OperationContext.java diff --git a/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestCreateCaseOperation.java b/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestCreateCaseOperation.java index 0200442778..93928f7678 100644 --- a/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestCreateCaseOperation.java +++ b/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestCreateCaseOperation.java @@ -17,7 +17,6 @@ import uk.gov.hmcts.ccd.domain.service.caselinking.CaseLinkService; import uk.gov.hmcts.ccd.domain.service.common.CaseDataService; import uk.gov.hmcts.ccd.domain.service.common.CasePostStateService; -import uk.gov.hmcts.ccd.domain.service.common.CaseTypeService; import uk.gov.hmcts.ccd.domain.service.common.EventTriggerService; import uk.gov.hmcts.ccd.domain.service.createcase.DefaultCreateCaseOperation; import uk.gov.hmcts.ccd.domain.service.createcase.SubmitCaseTransaction; @@ -25,6 +24,7 @@ import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker; import uk.gov.hmcts.ccd.domain.service.supplementarydata.SupplementaryDataUpdateOperation; import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger; +import uk.gov.hmcts.ccd.domain.service.validate.DefaultValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser; @@ -46,8 +46,9 @@ public ContractTestCreateCaseOperation(@Qualifier(DefaultUserRepository.QUALIFIE EventTokenService eventTokenService, CaseDataService caseDataService, SubmitCaseTransaction submitCaseTransaction, - CaseSanitiser caseSanitiser, CaseTypeService caseTypeService, + CaseSanitiser caseSanitiser, CallbackInvoker callbackInvoker, + @Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) ValidateCaseFieldsOperation validateCaseFieldsOperation, CasePostStateService casePostStateService, @Qualifier(DefaultDraftGateway.QUALIFIER) DraftGateway draftGateway, @@ -59,7 +60,7 @@ public ContractTestCreateCaseOperation(@Qualifier(DefaultUserRepository.QUALIFIE SupplementaryDataUpdateRequestValidator supplementaryDataValidator, CaseLinkService caseLinkService) { super(userRepository, caseDefinitionRepository, eventTriggerService, eventTokenService, caseDataService, - submitCaseTransaction, caseSanitiser, caseTypeService, callbackInvoker, validateCaseFieldsOperation, + submitCaseTransaction, caseSanitiser, callbackInvoker, validateCaseFieldsOperation, casePostStateService, draftGateway, caseDataIssueLogger, globalSearchProcessorService, supplementaryDataUpdateOperation, supplementaryDataValidator, caseLinkService); this.contractTestSecurityUtils = contractTestSecurityUtils; diff --git a/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java b/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java index 049a17ca6e..9a4d3bc03f 100644 --- a/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java +++ b/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java @@ -27,6 +27,8 @@ public final class JacksonUtils { + public static final String DATA = "data"; + private JacksonUtils() { } @@ -198,4 +200,12 @@ private static String getValue(@NonNull JsonNode jsonNode) { } return returnValue; } + + public static Map convertValueInDataField(Object from) { + Map map = MAPPER.convertValue(from, new TypeReference>() {}); + + Map dataNode = new HashMap<>(); + dataNode.put(DATA, MAPPER.valueToTree(map)); + return dataNode; + } } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperation.java index 208d6c195c..b8373f7f68 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperation.java @@ -29,12 +29,13 @@ import uk.gov.hmcts.ccd.domain.service.caselinking.CaseLinkService; import uk.gov.hmcts.ccd.domain.service.common.CaseDataService; import uk.gov.hmcts.ccd.domain.service.common.CasePostStateService; -import uk.gov.hmcts.ccd.domain.service.common.CaseTypeService; import uk.gov.hmcts.ccd.domain.service.common.EventTriggerService; import uk.gov.hmcts.ccd.domain.service.processor.GlobalSearchProcessorService; import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker; import uk.gov.hmcts.ccd.domain.service.supplementarydata.SupplementaryDataUpdateOperation; import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger; +import uk.gov.hmcts.ccd.domain.service.validate.DefaultValidateCaseFieldsOperation; +import uk.gov.hmcts.ccd.domain.service.validate.OperationContext; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser; import uk.gov.hmcts.ccd.endpoint.exceptions.CallbackException; @@ -57,7 +58,6 @@ public class DefaultCreateCaseOperation implements CreateCaseOperation { private final CaseDataService caseDataService; private final SubmitCaseTransaction submitCaseTransaction; private final CaseSanitiser caseSanitiser; - private final CaseTypeService caseTypeService; private final CallbackInvoker callbackInvoker; private final ValidateCaseFieldsOperation validateCaseFieldsOperation; private final DraftGateway draftGateway; @@ -77,8 +77,8 @@ public DefaultCreateCaseOperation(@Qualifier(CachedUserRepository.QUALIFIER) fin final CaseDataService caseDataService, final SubmitCaseTransaction submitCaseTransaction, final CaseSanitiser caseSanitiser, - final CaseTypeService caseTypeService, final CallbackInvoker callbackInvoker, + @Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) final ValidateCaseFieldsOperation validateCaseFieldsOperation, final CasePostStateService casePostStateService, @Qualifier(CachedDraftGateway.QUALIFIER) final DraftGateway draftGateway, @@ -94,7 +94,6 @@ public DefaultCreateCaseOperation(@Qualifier(CachedUserRepository.QUALIFIER) fin this.eventTokenService = eventTokenService; this.submitCaseTransaction = submitCaseTransaction; this.caseSanitiser = caseSanitiser; - this.caseTypeService = caseTypeService; this.caseDataService = caseDataService; this.callbackInvoker = callbackInvoker; this.validateCaseFieldsOperation = validateCaseFieldsOperation; @@ -140,7 +139,7 @@ public CaseDetails createCaseDetails(final String caseTypeId, caseTypeDefinition.getJurisdictionDefinition(), caseTypeDefinition); - validateCaseFieldsOperation.validateCaseDetails(caseTypeId, caseDataContent); + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(caseTypeId, caseDataContent)); final CaseDetails newCaseDetails = new CaseDetails(); diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java index b667379b6e..fcf5230298 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java @@ -43,6 +43,7 @@ import uk.gov.hmcts.ccd.domain.service.stdapi.AboutToSubmitCallbackResponse; import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker; import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger; +import uk.gov.hmcts.ccd.domain.service.validate.DefaultValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser; import uk.gov.hmcts.ccd.endpoint.exceptions.BadRequestException; @@ -113,6 +114,7 @@ public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final U final CallbackInvoker callbackInvoker, final UIDService uidService, final SecurityClassificationServiceImpl securityClassificationService, + @Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) final ValidateCaseFieldsOperation validateCaseFieldsOperation, final UserAuthorisation userAuthorisation, final FieldProcessorService fieldProcessorService, diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java new file mode 100644 index 0000000000..1076fd8971 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java @@ -0,0 +1,105 @@ +package uk.gov.hmcts.ccd.domain.service.validate; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.config.JacksonUtils; +import uk.gov.hmcts.ccd.data.definition.CachedCaseDefinitionRepository; +import uk.gov.hmcts.ccd.data.definition.CaseDefinitionRepository; +import uk.gov.hmcts.ccd.domain.model.casedataaccesscontrol.AccessProfile; +import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition; +import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; +import uk.gov.hmcts.ccd.domain.service.common.AccessControlService; +import uk.gov.hmcts.ccd.domain.service.common.CaseAccessService; +import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; +import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; + +import java.util.Map; +import java.util.Set; + +import static com.google.common.collect.Maps.newHashMap; +import static uk.gov.hmcts.ccd.domain.service.common.AccessControlService.CAN_READ; + +@Service +@Slf4j +@Qualifier(AuthorisedValidateCaseFieldsOperation.QUALIFIER) +public class AuthorisedValidateCaseFieldsOperation implements ValidateCaseFieldsOperation { + public static final String QUALIFIER = "authorised"; + + private final AccessControlService accessControlService; + private final CaseDefinitionRepository caseDefinitionRepository; + private final CaseAccessService caseAccessService; + private final ValidateCaseFieldsOperation validateCaseFieldsOperation; + private final MidEventCallback midEventCallback; + + public AuthorisedValidateCaseFieldsOperation(AccessControlService accessControlService, + @Qualifier(CachedCaseDefinitionRepository.QUALIFIER) + CaseDefinitionRepository caseDefinitionRepository, + CaseAccessService caseAccessService, + @Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) + ValidateCaseFieldsOperation validateCaseFieldsOperation, + MidEventCallback midEventCallback) { + this.accessControlService = accessControlService; + this.caseDefinitionRepository = caseDefinitionRepository; + this.caseAccessService = caseAccessService; + this.validateCaseFieldsOperation = validateCaseFieldsOperation; + this.midEventCallback = midEventCallback; + } + + @Override + public Map validateCaseDetails(OperationContext operationContext) { + validateCaseFieldsOperation.validateCaseDetails(operationContext); + + CaseDataContent content = operationContext.content(); + String caseTypeId = operationContext.caseTypeId(); + String pageId = operationContext.pageId(); + + final JsonNode data = midEventCallback.invoke(caseTypeId, + content, + pageId); + + content.setData(JacksonUtils.convertValue(data)); + + verifyReadAccess(caseTypeId, content); + + return content.getData(); + } + + @Override + public void validateData(Map data, CaseTypeDefinition caseTypeDefinition, + CaseDataContent content) { + validateCaseFieldsOperation.validateData(data, caseTypeDefinition, content); + } + + private void verifyReadAccess(final String caseTypeId, CaseDataContent content) { + final CaseTypeDefinition caseTypeDefinition = getCaseDefinitionType(caseTypeId); + + Set caseAccessProfiles = + caseAccessService.getAccessProfilesByCaseReference(content.getCaseReference()); + + if (!accessControlService.canAccessCaseTypeWithCriteria( + caseTypeDefinition, + caseAccessProfiles, + CAN_READ)) { + content.setData(newHashMap()); + return; + } + + content.setData(JacksonUtils.convertValueInDataField( + accessControlService.filterCaseFieldsByAccess( + JacksonUtils.convertValueJsonNode(content.getData().get(JacksonUtils.DATA)), + caseTypeDefinition.getCaseFieldDefinitions(), + caseAccessProfiles, + CAN_READ, + false))); + } + + private CaseTypeDefinition getCaseDefinitionType(String caseTypeId) { + final CaseTypeDefinition caseTypeDefinition = caseDefinitionRepository.getCaseType(caseTypeId); + if (caseTypeDefinition == null) { + throw new ValidationException("Cannot find case type definition for " + caseTypeId); + } + return caseTypeDefinition; + } +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperation.java index 6ef8b6d1e0..c6ad752e01 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperation.java @@ -16,7 +16,9 @@ import java.util.Map; @Service +@Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) public class DefaultValidateCaseFieldsOperation implements ValidateCaseFieldsOperation { + public static final String QUALIFIER = "default"; private final CaseDefinitionRepository caseDefinitionRepository; private final CaseTypeService caseTypeService; @@ -35,10 +37,12 @@ public class DefaultValidateCaseFieldsOperation implements ValidateCaseFieldsOpe } @Override - public final Map validateCaseDetails(String caseTypeId, CaseDataContent content) { + public final Map validateCaseDetails(OperationContext operationContext) { + CaseDataContent content = operationContext.content(); if (content == null || content.getEvent() == null || content.getEventId() == null) { throw new ValidationException("Cannot validate case field because of event is not specified"); } + String caseTypeId = operationContext.caseTypeId(); final CaseTypeDefinition caseTypeDefinition = caseDefinitionRepository.getCaseType(caseTypeId); if (caseTypeDefinition == null) { throw new ValidationException("Cannot find case type definition for " + caseTypeId); diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/OperationContext.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/OperationContext.java new file mode 100644 index 0000000000..d19eb6d4da --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/OperationContext.java @@ -0,0 +1,9 @@ +package uk.gov.hmcts.ccd.domain.service.validate; + +import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; + +public record OperationContext(String caseTypeId, CaseDataContent content, String pageId) { + public OperationContext(String caseTypeId, CaseDataContent content) { + this(caseTypeId, content, null); + } +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java index d1e84b4e54..41e8fe44b0 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java @@ -7,8 +7,8 @@ import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; public interface ValidateCaseFieldsOperation { - Map validateCaseDetails(String caseTypeId, - final CaseDataContent content); + + Map validateCaseDetails(OperationContext validationContext); void validateData(final Map data, final CaseTypeDefinition caseTypeDefinition, diff --git a/src/main/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpoint.java b/src/main/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpoint.java index 2d4669ea5f..f70c80d432 100644 --- a/src/main/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpoint.java +++ b/src/main/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpoint.java @@ -22,6 +22,7 @@ import uk.gov.hmcts.ccd.appinsights.AppInsights; import uk.gov.hmcts.ccd.auditlog.AuditOperationType; import uk.gov.hmcts.ccd.auditlog.LogAudit; +import uk.gov.hmcts.ccd.config.JacksonUtils; import uk.gov.hmcts.ccd.data.casedetails.SecurityClassification; import uk.gov.hmcts.ccd.data.casedetails.search.FieldMapSanitizeOperation; import uk.gov.hmcts.ccd.data.casedetails.search.MetaData; @@ -32,7 +33,6 @@ import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; import uk.gov.hmcts.ccd.domain.service.createcase.CreateCaseOperation; import uk.gov.hmcts.ccd.domain.service.createevent.CreateEventOperation; -import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; import uk.gov.hmcts.ccd.domain.service.getcase.CaseNotFoundException; import uk.gov.hmcts.ccd.domain.service.getcase.CreatorGetCaseOperation; import uk.gov.hmcts.ccd.domain.service.getcase.GetCaseOperation; @@ -41,6 +41,8 @@ import uk.gov.hmcts.ccd.domain.service.search.SearchOperation; import uk.gov.hmcts.ccd.domain.service.startevent.StartEventOperation; import uk.gov.hmcts.ccd.domain.service.stdapi.DocumentsOperation; +import uk.gov.hmcts.ccd.domain.service.validate.AuthorisedValidateCaseFieldsOperation; +import uk.gov.hmcts.ccd.domain.service.validate.OperationContext; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.endpoint.exceptions.ApiException; import uk.gov.hmcts.ccd.endpoint.exceptions.BadRequestException; @@ -82,7 +84,6 @@ public class CaseDetailsEndpoint { private final AppInsights appInsights; private final FieldMapSanitizeOperation fieldMapSanitizeOperation; private final ValidateCaseFieldsOperation validateCaseFieldsOperation; - private final MidEventCallback midEventCallback; @Autowired public CaseDetailsEndpoint(@Qualifier(CreatorGetCaseOperation.QUALIFIER) final GetCaseOperation getCaseOperation, @@ -91,10 +92,10 @@ public CaseDetailsEndpoint(@Qualifier(CreatorGetCaseOperation.QUALIFIER) final G @Qualifier("authorised") final StartEventOperation startEventOperation, @Qualifier(AuthorisedSearchOperation.QUALIFIER) final SearchOperation searchOperation, final FieldMapSanitizeOperation fieldMapSanitizeOperation, + @Qualifier(AuthorisedValidateCaseFieldsOperation.QUALIFIER) final ValidateCaseFieldsOperation validateCaseFieldsOperation, final DocumentsOperation documentsOperation, final PaginatedSearchMetaDataOperation paginatedSearchMetaDataOperation, - final MidEventCallback midEventCallback, final AppInsights appinsights) { this.getCaseOperation = getCaseOperation; this.createCaseOperation = createCaseOperation; @@ -105,7 +106,6 @@ public CaseDetailsEndpoint(@Qualifier(CreatorGetCaseOperation.QUALIFIER) final G this.documentsOperation = documentsOperation; this.validateCaseFieldsOperation = validateCaseFieldsOperation; this.paginatedSearchMetaDataOperation = paginatedSearchMetaDataOperation; - this.midEventCallback = midEventCallback; this.appInsights = appinsights; } @@ -333,12 +333,8 @@ public JsonNode validateCaseDetails( @RequestParam(required = false) final String pageId, @RequestBody final CaseDataContent content) { - validateCaseFieldsOperation.validateCaseDetails(caseTypeId, - content); - - return midEventCallback.invoke(caseTypeId, - content, - pageId); + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(caseTypeId, content, pageId)); + return JacksonUtils.convertValueJsonNode(content.getData()); } @PostMapping(value = "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/cases/{cid}/events") 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..c4f17cb49b 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 @@ -1,13 +1,12 @@ package uk.gov.hmcts.ccd.v2.external.controller; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -15,9 +14,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; 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.createevent.MidEventCallback; +import uk.gov.hmcts.ccd.domain.service.validate.AuthorisedValidateCaseFieldsOperation; +import uk.gov.hmcts.ccd.domain.service.validate.OperationContext; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.v2.V2; import uk.gov.hmcts.ccd.v2.external.resource.CaseDataResource; @@ -25,16 +24,13 @@ @RestController @RequestMapping(path = "/case-types") public class CaseDataValidatorController { - private static final ObjectMapper MAPPER = JacksonUtils.MAPPER; private final ValidateCaseFieldsOperation validateCaseFieldsOperation; - private final MidEventCallback midEventCallback; @Autowired public CaseDataValidatorController( - ValidateCaseFieldsOperation validateCaseFieldsOperation, - MidEventCallback midEventCallback) { + @Qualifier(AuthorisedValidateCaseFieldsOperation.QUALIFIER) + ValidateCaseFieldsOperation validateCaseFieldsOperation) { this.validateCaseFieldsOperation = validateCaseFieldsOperation; - this.midEventCallback = midEventCallback; } @PostMapping( @@ -71,14 +67,7 @@ public CaseDataValidatorController( public ResponseEntity validate(@PathVariable("caseTypeId") String caseTypeId, @RequestParam(required = false) final String pageId, @RequestBody final CaseDataContent content) { - validateCaseFieldsOperation.validateCaseDetails(caseTypeId, - content); - - final JsonNode data = midEventCallback.invoke(caseTypeId, - content, - pageId); - - content.setData(JacksonUtils.convertValue(data)); + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(caseTypeId, content, pageId)); return ResponseEntity.ok(new CaseDataResource(content, caseTypeId, pageId)); } } diff --git a/src/main/java/uk/gov/hmcts/ccd/v2/external/resource/CaseDataResource.java b/src/main/java/uk/gov/hmcts/ccd/v2/external/resource/CaseDataResource.java index 26463fc7ca..2653270d6c 100644 --- a/src/main/java/uk/gov/hmcts/ccd/v2/external/resource/CaseDataResource.java +++ b/src/main/java/uk/gov/hmcts/ccd/v2/external/resource/CaseDataResource.java @@ -29,6 +29,6 @@ public CaseDataResource(@NonNull CaseDataContent caseData, String caseTypeId, St } private void copyProperties(CaseDataContent caseData) { - this.data = JacksonUtils.convertValueJsonNode(caseData.getData().get("data")); + this.data = JacksonUtils.convertValueJsonNode(caseData.getData().get(JacksonUtils.DATA)); } } diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java index ace1e2b05f..2894c1cfd8 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java @@ -38,6 +38,7 @@ import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker; import uk.gov.hmcts.ccd.domain.service.supplementarydata.SupplementaryDataUpdateOperation; import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger; +import uk.gov.hmcts.ccd.domain.service.validate.OperationContext; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser; import uk.gov.hmcts.ccd.endpoint.exceptions.CallbackException; @@ -149,8 +150,7 @@ void setup() throws Exception { caseDataService, submitCaseTransaction, caseSanitiser, - caseTypeService, - callbackInvoker, + callbackInvoker, validateCaseFieldsOperation, casePostStateService, draftGateway, @@ -271,7 +271,8 @@ void shouldNotTryToDeleteDraftIfNoDraftIdSet() { given(eventTriggerService.isPreStateValid(null, eventTrigger)).willReturn(Boolean.TRUE); given(savedCaseType.getState()).willReturn(caseEventStateId); given(caseTypeService.findState(CASE_TYPE, caseEventStateId)).willReturn(caseEventState); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)).willReturn(data); + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) + .willReturn(data); given(submitCaseTransaction.submitCase(same(event), same(CASE_TYPE), same(IDAM_USER), @@ -301,7 +302,8 @@ void shouldCallSaveSupplementaryDataWhenValidDataPassed() { given(savedCaseType.getState()).willReturn(caseEventStateId); given(savedCaseType.getReferenceAsString()).willReturn("1234567"); given(caseTypeService.findState(CASE_TYPE, caseEventStateId)).willReturn(caseEventState); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)).willReturn(data); + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) + .willReturn(data); given(submitCaseTransaction.submitCase(same(event), same(CASE_TYPE), same(IDAM_USER), @@ -332,7 +334,8 @@ void shouldNotCallSaveSupplementaryDataWhenValidDataPassed() { given(eventTriggerService.isPreStateValid(null, eventTrigger)).willReturn(Boolean.TRUE); given(savedCaseType.getState()).willReturn(caseEventStateId); given(caseTypeService.findState(CASE_TYPE, caseEventStateId)).willReturn(caseEventState); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)).willReturn(data); + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) + .willReturn(data); given(submitCaseTransaction.submitCase(same(event), same(CASE_TYPE), same(IDAM_USER), @@ -363,7 +366,8 @@ void shouldSetIncompleteDeleteDraftWhenDeleteDraftThrowsException() { given(eventTriggerService.isPreStateValid(null, eventTrigger)).willReturn(Boolean.TRUE); given(savedCaseType.getState()).willReturn(caseEventStateId); given(caseTypeService.findState(CASE_TYPE, caseEventStateId)).willReturn(caseEventState); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)).willReturn(data); + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) + .willReturn(data); given(submitCaseTransaction.submitCase(same(event), same(CASE_TYPE), same(IDAM_USER), @@ -388,7 +392,7 @@ void shouldReturnSavedCaseDetails_whenSubmittedCallBackUrlIsBlank() { given(eventTriggerService.isPreStateValid(null, eventTrigger)).willReturn(Boolean.TRUE); given(savedCaseType.getState()).willReturn(caseEventStateId); given(caseTypeService.findState(CASE_TYPE, caseEventStateId)).willReturn(caseEventState); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)) + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) .willReturn(data); given(submitCaseTransaction.submitCase(same(event), same(CASE_TYPE), @@ -418,7 +422,8 @@ void shouldReturnSavedCaseDetails_whenSubmittedCallBackUrlIsBlank() { () -> assertThat(caseDetails, IsInstanceOf.instanceOf(CaseDetails.class)), () -> order.verify(eventTokenService).validateToken(TOKEN, UID, eventTrigger, CASE_TYPE.getJurisdictionDefinition(), CASE_TYPE), - () -> order.verify(validateCaseFieldsOperation).validateCaseDetails(CASE_TYPE_ID, eventData), + () -> order.verify(validateCaseFieldsOperation) + .validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData)), () -> order.verify(globalSearchProcessorService).populateGlobalSearchData(CASE_TYPE, eventData.getData()), () -> order.verify(submitCaseTransaction).submitCase(same(event), same(CASE_TYPE), @@ -448,7 +453,7 @@ void shouldReturnSavedCaseDetails_whenCallBackFails() { given(callbackInvoker.invokeSubmittedCallback(eventTrigger, null, savedCaseType)) .willThrow(new CallbackException("call back exception")); given( - validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)) + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) .willReturn(data); given( submitCaseTransaction.submitCase( @@ -478,7 +483,8 @@ void shouldReturnSavedCaseDetails_whenCallBackFails() { assertAll("case details saved when call back fails", () -> order.verify(eventTokenService).validateToken(TOKEN, UID, eventTrigger, CASE_TYPE.getJurisdictionDefinition(), CASE_TYPE), - () -> order.verify(validateCaseFieldsOperation).validateCaseDetails(CASE_TYPE_ID, eventData), + () -> order.verify(validateCaseFieldsOperation) + .validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData)), () -> order.verify(globalSearchProcessorService).populateGlobalSearchData(CASE_TYPE, eventData.getData()), () -> order.verify(submitCaseTransaction).submitCase(same(event), same(CASE_TYPE), @@ -513,7 +519,7 @@ void shouldReturnAlsoCallbackResponse_whenCallBackIsInvokedSuccessfully() { given(response.getBody()).willReturn(responseBody); given(response.getStatusCodeValue()).willReturn(200); given(savedCaseType.getCaseTypeId()).willReturn(mockCaseTypeId); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)) + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) .willReturn(data); given(submitCaseTransaction.submitCase( same(event), @@ -542,7 +548,8 @@ void shouldReturnAlsoCallbackResponse_whenCallBackIsInvokedSuccessfully() { () -> assertThat(caseDetails.getCaseTypeId(), is(mockCaseTypeId)), () -> order.verify(eventTokenService).validateToken(TOKEN, UID, eventTrigger, CASE_TYPE.getJurisdictionDefinition(), CASE_TYPE), - () -> order.verify(validateCaseFieldsOperation).validateCaseDetails(CASE_TYPE_ID, eventData), + () -> order.verify(validateCaseFieldsOperation) + .validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData)), () -> order.verify(globalSearchProcessorService).populateGlobalSearchData(CASE_TYPE, eventData.getData()), () -> order.verify(submitCaseTransaction).submitCase(same(event), same(CASE_TYPE), @@ -581,7 +588,7 @@ void shouldInsertCaseLinks() { given(savedCaseType.getCaseTypeId()).willReturn(CASE_TYPE_ID); given(savedCaseType.getId()).willReturn(CASE_ID); given(savedCaseType.getData()).willReturn(data); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)) + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))) .willReturn(data); given(submitCaseTransaction.submitCase( same(event), @@ -610,7 +617,8 @@ void shouldInsertCaseLinks() { () -> assertThat(caseDetails.getCaseTypeId(), is(CASE_TYPE_ID)), () -> order.verify(eventTokenService).validateToken(TOKEN, UID, eventTrigger, CASE_TYPE.getJurisdictionDefinition(), CASE_TYPE), - () -> order.verify(validateCaseFieldsOperation).validateCaseDetails(CASE_TYPE_ID, eventData), + () -> order.verify(validateCaseFieldsOperation) + .validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData)), () -> order.verify(submitCaseTransaction).submitCase(same(event), same(CASE_TYPE), same(IDAM_USER), diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperationTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperationTest.java index 4aa361e39a..9cc1a4a7a0 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperationTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/DefaultValidateCaseFieldsOperationTest.java @@ -105,7 +105,7 @@ void shouldValidate_when_organisation_has_correct_role() throws Exception { doReturn(organisationPolicyData).when(caseDataContent).getData(); final Map result = - validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent); + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, caseDataContent)); assertAll( () -> assertThat(result, is(organisationPolicyData)) @@ -123,7 +123,7 @@ void shouldValidate_when_organisation_has_correct_roles() throws Exception { doReturn(organisationPolicyData).when(caseDataContent).getData(); final Map result = - validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent); + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, caseDataContent)); assertAll( () -> assertThat(result, is(organisationPolicyData)) @@ -197,7 +197,7 @@ void shouldValidate_when_organisation_has_correct_roles_complex() throws Excepti doReturn(parentData).when(caseDataContent).getData(); final Map result = - validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent); + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, caseDataContent)); assertAll( () -> assertThat(result, is(parentData)) @@ -220,7 +220,8 @@ void should_fail_validate_when_organisation_has_null_role() throws Exception { CaseValidationException caseValidationExceptionResult = assertThrows(CaseValidationException.class, - () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent)); + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(caseValidationExceptionResult.getMessage(), containsString("Case data validation failed")); @@ -243,7 +244,8 @@ void should_fail_validate_when_organisation_has_incorrect_role() throws Exceptio CaseValidationException caseValidationExceptionResult = assertThrows(CaseValidationException.class, - () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent)); + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(caseValidationExceptionResult.getMessage(), @@ -278,7 +280,8 @@ void should_fail_validateData_when_organisation_has_null_role() throws Exception CaseValidationException caseValidationExceptionResult = assertThrows(CaseValidationException.class, - () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent)); + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(caseValidationExceptionResult.getMessage(), @@ -301,7 +304,8 @@ void should_fail_validateData_when_organisation_has_incorrect_role() throws Exce CaseValidationException caseValidationExceptionResult = assertThrows(CaseValidationException.class, - () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent)); + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(caseValidationExceptionResult.getMessage(), containsString("Case data validation failed")); @@ -309,7 +313,8 @@ void should_fail_validateData_when_organisation_has_incorrect_role() throws Exce @Test void shouldValidateCaseDetails() { - Map result = validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, caseDataContent); + Map result = + validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, caseDataContent)); assertAll( () -> verify(caseDefinitionRepository, times(1)).getCaseType(CASE_TYPE_ID), @@ -321,8 +326,8 @@ void shouldValidateCaseDetails() { @Test void shouldFailValidationIfNoContent() { ValidationException exception = - assertThrows(ValidationException.class, () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, - null)); + assertThrows(ValidationException.class, + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID,null))); assertThat(exception.getMessage(), startsWith("Cannot validate case field because of event is not specified")); verify(caseDefinitionRepository, never()).getCaseType(any()); @@ -334,8 +339,9 @@ void shouldFailValidationIfNoEventInContent() { doReturn(null).when(caseDataContent).getEvent(); ValidationException exception = - assertThrows(ValidationException.class, () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, - caseDataContent)); + assertThrows(ValidationException.class, + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(exception.getMessage(), startsWith("Cannot validate case field because of event is not specified")); verify(caseDefinitionRepository, never()).getCaseType(any()); @@ -347,8 +353,9 @@ void shouldFailValidationIfNoEventIdInContent() { doReturn(null).when(caseDataContent).getEventId(); ValidationException exception = - assertThrows(ValidationException.class, () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, - caseDataContent)); + assertThrows(ValidationException.class, + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(exception.getMessage(), startsWith("Cannot validate case field because of event is not specified")); verify(caseDefinitionRepository, never()).getCaseType(any()); @@ -360,8 +367,9 @@ void shouldFailValidationIfNoCaseType() { doReturn(null).when(caseDefinitionRepository).getCaseType(CASE_TYPE_ID); ValidationException exception = - assertThrows(ValidationException.class, () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, - caseDataContent)); + assertThrows(ValidationException.class, + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(exception.getMessage(), startsWith("Cannot find case type definition for " + CASE_TYPE_ID)); verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID); @@ -373,8 +381,9 @@ void shouldFailValidationIfEventIdNotFoundInCaseType() { doReturn("otherEvent").when(caseDataContent).getEventId(); ValidationException exception = - assertThrows(ValidationException.class, () -> validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, - caseDataContent)); + assertThrows(ValidationException.class, + () -> validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, + caseDataContent))); assertThat(exception.getMessage(), startsWith("Cannot validate case field because of event otherEvent is not found in case type definition")); verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID); diff --git a/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointIT.java b/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointIT.java index 8f39f546f3..2c41da7988 100644 --- a/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointIT.java +++ b/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointIT.java @@ -88,7 +88,6 @@ import static uk.gov.hmcts.ccd.data.casedetails.SecurityClassification.PRIVATE; import static uk.gov.hmcts.ccd.data.caselinking.CaseLinkEntity.NON_STANDARD_LINK; import static uk.gov.hmcts.ccd.domain.model.casedeletion.TTL.TTL_CASE_FIELD_ID; -import static uk.gov.hmcts.ccd.domain.model.caselinking.CaseLink.builder; import static uk.gov.hmcts.ccd.domain.model.std.EventBuilder.anEvent; import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.CaseDataContentBuilder.newCaseDataContent; import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.CaseViewFieldBuilder.aViewField; @@ -3932,19 +3931,19 @@ public void shouldReturn201AndInsertCaseLinksWhenCreateCaseEvent() Long expectedCaseId = CASE_22_ID; List expectedCaseLinks = List.of( - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_01_ID) .caseTypeId(CASE_01_TYPE) .standardLink(NON_STANDARD_LINK) .build(), - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_02_ID) .caseTypeId(CASE_02_TYPE) .standardLink(NON_STANDARD_LINK) .build(), - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_03_ID) // NB: previously added in "classpath:sql/insert_cases.sql" .caseTypeId(CASE_03_TYPE) @@ -3992,7 +3991,7 @@ public void shouldReturn201AndDeleteCaseLinksWhenCreateCaseEvent() Long expectedCaseId = CASE_22_ID; List expectedCaseLinks = List.of( - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_01_ID) .caseTypeId(CASE_01_TYPE) @@ -4375,7 +4374,9 @@ private void shouldReturn201WhenPostCreateCaseEventWithExistingDocumentBinary(St } @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) public void shouldReturn200WhenPostValidateCaseDetailsWithValidDataForCaseworker() throws Exception { + final String caseReference = "1504259907353545"; final JsonNode DATA = mapper.readTree(exampleData()); final CaseDataContent caseDetailsToValidate = newCaseDataContent() @@ -4384,6 +4385,7 @@ public void shouldReturn200WhenPostValidateCaseDetailsWithValidDataForCaseworker .withSummary(SHORT_COMMENT) .withDescription(LONG_COMMENT) .build()) + .withCaseReference(caseReference) .withToken(generateEventTokenNewCase(UID, JURISDICTION, CASE_TYPE, TEST_EVENT_ID)) .withData(JacksonUtils.convertValue(DATA)) .withIgnoreWarning(Boolean.FALSE) @@ -4403,7 +4405,9 @@ public void shouldReturn200WhenPostValidateCaseDetailsWithValidDataForCaseworker } @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) public void shouldReturnWhenPostValidateCaseDetailsWithValidDataForCaseworker() throws Exception { + final String caseReference = "1504259907353545"; final JsonNode DATA = mapper.readTree(exampleData()); final CaseDataContent caseDetailsToValidate = newCaseDataContent() @@ -4412,6 +4416,7 @@ public void shouldReturnWhenPostValidateCaseDetailsWithValidDataForCaseworker() .withSummary(SHORT_COMMENT) .withDescription(LONG_COMMENT) .build()) + .withCaseReference(caseReference) .withToken(generateEventTokenNewCase(UID, JURISDICTION, CASE_TYPE, TEST_EVENT_ID)) .withData(JacksonUtils.convertValue(DATA)) .withIgnoreWarning(Boolean.FALSE) @@ -4462,7 +4467,9 @@ public void shouldReturn422WhenPostValidateCaseDetailsWithInvalidDataForCasework } @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) public void shouldReturn200WhenPostValidateCaseDetailsWithValidDataForCitizen() throws Exception { + final String caseReference = "1504259907353545"; final JsonNode DATA = mapper.readTree(exampleData()); final CaseDataContent caseDetailsToValidate = newCaseDataContent() @@ -4471,6 +4478,7 @@ public void shouldReturn200WhenPostValidateCaseDetailsWithValidDataForCitizen() .withSummary(SHORT_COMMENT) .withDescription(LONG_COMMENT) .build()) + .withCaseReference(caseReference) .withToken(generateEventTokenNewCase(UID, JURISDICTION, CASE_TYPE, TEST_EVENT_ID)) .withData(JacksonUtils.convertValue(DATA)) .withIgnoreWarning(Boolean.FALSE) @@ -5300,7 +5308,10 @@ private String exampleData() { } @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) public void shouldFilterCaseDataWhoseOrderGreaterThanPassedPageId() throws Exception { + final String caseReference = "1504259907353545"; + final JsonNode data = mapper.readTree(exampleCaseData()); final JsonNode eventData = mapper.readTree(exampleEventData()); WizardPageCollection wizardPageCollection = createWizardPageCollection(MID_EVENT_CALL_BACK); @@ -5315,6 +5326,7 @@ public void shouldFilterCaseDataWhoseOrderGreaterThanPassedPageId() throws Excep .withSummary(summary) .withDescription(description) .build()) + .withCaseReference(caseReference) .withToken(generateEventTokenNewCase(UID, JURISDICTION, CASE_TYPE_VALIDATE, TEST_EVENT_ID)) .withData(mapper.convertValue(data, new TypeReference>() {})) .withEventData(mapper.convertValue(eventData, new TypeReference>() {})) @@ -5393,7 +5405,9 @@ public void shouldReturn400WhenPostValidateCaseDetailsMidEventCallbackChangesDat } @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) public void shouldFilterCaseDataWhoseOrderGreaterThanPassedPageIdMultiplePreviousPages() throws Exception { + final String caseReference = "1504259907353529"; final JsonNode data = mapper.readTree(secondPageData()); final JsonNode eventData = mapper.readTree(exampleEventDataMultiPages()); WizardPageCollection wizardPageCollection = createWizardPageCollection(MID_EVENT_CALL_BACK_MULTI_PAGE); @@ -5418,6 +5432,7 @@ public void shouldFilterCaseDataWhoseOrderGreaterThanPassedPageIdMultiplePreviou .withSummary(summary) .withDescription(description) .build()) + .withCaseReference(caseReference) .withToken(generateEventTokenNewCase(UID, JURISDICTION, CASE_TYPE_VALIDATE_MULTI_PAGE, TEST_EVENT_ID)) .withData(mapper.convertValue(data, new TypeReference>() {})) .withEventData(mapper.convertValue(eventData, new TypeReference>() {})) @@ -5499,13 +5514,13 @@ private void shouldReturn201WithCaseLinksInsertedInDbWhenPostCreateCaseEventWith Long expectedCaseId = 1L; List expectedCaseLinks = List.of( - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_LINKS_CASE_999_ID) .caseTypeId(CASE_LINKS_CASE_999_TYPE) .standardLink(NON_STANDARD_LINK) .build(), - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_LINKS_CASE_998_ID) .caseTypeId(CASE_LINKS_CASE_998_TYPE) @@ -5548,12 +5563,12 @@ public void shouldReturn422BadRequestWhenCaseLinksSpecifiedDoesNotExist() Long expectedCaseId = 1L; List expectedCaseLinks = List.of( - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_LINKS_CASE_999_ID) .caseTypeId(CASE_LINKS_CASE_999_TYPE) .build(), - builder() + CaseLink.builder() .caseId(expectedCaseId) .linkedCaseId(CASE_LINKS_CASE_998_ID) .caseTypeId(CASE_LINKS_CASE_998_TYPE) diff --git a/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointTest.java b/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointTest.java index 7e1f829b84..06234a1a47 100644 --- a/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/endpoint/std/CaseDetailsEndpointTest.java @@ -1,22 +1,5 @@ package uk.gov.hmcts.ccd.endpoint.std; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static com.google.common.collect.Maps.newHashMap; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; -import static uk.gov.hmcts.ccd.domain.model.std.EventBuilder.anEvent; -import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.CaseDataContentBuilder.newCaseDataContent; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -36,16 +19,35 @@ import uk.gov.hmcts.ccd.domain.model.std.Event; import uk.gov.hmcts.ccd.domain.service.createcase.CreateCaseOperation; import uk.gov.hmcts.ccd.domain.service.createevent.CreateEventOperation; -import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; import uk.gov.hmcts.ccd.domain.service.getcase.CaseNotFoundException; import uk.gov.hmcts.ccd.domain.service.getcase.ClassifiedGetCaseOperation; import uk.gov.hmcts.ccd.domain.service.search.PaginatedSearchMetaDataOperation; import uk.gov.hmcts.ccd.domain.service.search.SearchOperation; import uk.gov.hmcts.ccd.domain.service.startevent.StartEventOperation; import uk.gov.hmcts.ccd.domain.service.stdapi.DocumentsOperation; +import uk.gov.hmcts.ccd.domain.service.validate.OperationContext; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.endpoint.exceptions.BadRequestException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static com.google.common.collect.Maps.newHashMap; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import static uk.gov.hmcts.ccd.domain.model.std.EventBuilder.anEvent; +import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.CaseDataContentBuilder.newCaseDataContent; + class CaseDetailsEndpointTest { private static final String UID = "1231"; @@ -91,9 +93,6 @@ class CaseDetailsEndpointTest { @Mock private ValidateCaseFieldsOperation validateCaseFieldsOperation; - @Mock - private MidEventCallback midEventCallback; - @Mock private AppInsights appInsights; @@ -116,8 +115,7 @@ void setUp() { validateCaseFieldsOperation, documentsOperation, paginatedSearchMetaDataOperation, - midEventCallback, - appInsights); + appInsights); } @Nested @@ -240,13 +238,8 @@ void validateCaseFieldsForCaseWorker() { objectNode.set("data", mapper.valueToTree(data)); final JsonNode toBeReturned = objectNode; - doReturn(data).when(validateCaseFieldsOperation).validateCaseDetails( - CASE_TYPE_ID, - EVENT_DATA); - doReturn(toBeReturned).when(midEventCallback).invoke( - CASE_TYPE_ID, - EVENT_DATA, - pageId); + OperationContext operationContext = new OperationContext(CASE_TYPE_ID, EVENT_DATA); + doReturn(data).when(validateCaseFieldsOperation).validateCaseDetails(operationContext); final JsonNode output = endpoint.validateCaseDetails( UID, @@ -256,14 +249,8 @@ void validateCaseFieldsForCaseWorker() { EVENT_DATA); assertAll( - () -> assertThat(output, sameInstance(toBeReturned)), - () -> verify(validateCaseFieldsOperation).validateCaseDetails( - CASE_TYPE_ID, - EVENT_DATA), - () -> verify(midEventCallback).invoke( - CASE_TYPE_ID, - EVENT_DATA, - pageId) + () -> assertNotEquals(output, toBeReturned), + () -> verify(validateCaseFieldsOperation).validateCaseDetails(any(OperationContext.class)) ); } diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/DCPTestHelper.java b/src/test/java/uk/gov/hmcts/ccd/v2/DCPTestHelper.java index 59a1966356..a36be0fe2b 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/DCPTestHelper.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/DCPTestHelper.java @@ -37,33 +37,35 @@ public static ArrayList> arrayOf(Object object) { } public static String validateContent() { - return "{\n" - + " \"data\": {\n" - + " \"TextField\": \"Case 1 Text\",\n" - + " \"CollectionComplexDateTime\": [\n" - + " {\n" - + " \"id\": \"ID\",\n" - + " \"value\": {\n" - + " \"DateField\": \"07-05-1963\",\n" - + " \"DateTimeField\": \"2008-04-02T16:37\",\n" - + " \"StandardDate\": \"1999-08-19\",\n" - + " \"StandardDateTime\": \"2010-06-17T19:20:00.000\",\n" - + " \"NestedComplex\": {\n" - + " \"DateField\": \"02-1981\",\n" - + " \"DateTimeField\": \"2002-03-04\",\n" - + " \"StandardDate\": \"2020-02-19\",\n" - + " \"StandardDateTime\": \"2007-07-17T07:07:00.000\"\n" - + " }\n" - + " }\n" - + " }\n" - + " ]\n" - + " },\n" - + " \"event\": {\n" - + " \"id\": \"UPDATE\",\n" - + " \"summary\": \"\",\n" - + " \"description\": \"\"\n" - + " }\n" - + "}"; + return """ + { + "data": { + "TextField": "Case 1 Text", + "CollectionComplexDateTime": [ + { + "id": "ID", + "value": { + "DateField": "07-05-1963", + "DateTimeField": "2008-04-02T16:37", + "StandardDate": "1999-08-19", + "StandardDateTime": "2010-06-17T19:20:00.000", + "NestedComplex": { + "DateField": "02-1981", + "DateTimeField": "2002-03-04", + "StandardDate": "2020-02-19", + "StandardDateTime": "2007-07-17T07:07:00.000" + } + } + } + ] + }, + "event": { + "id": "UPDATE", + "summary": "", + "description": "" + }, + "case_reference": "1587051668000989" + }"""; } public static String invalidValidateContent() { diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java b/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java index 53ec6226b7..84490d4113 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java @@ -15,6 +15,7 @@ import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; import uk.gov.hmcts.ccd.domain.model.std.Event; import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; +import uk.gov.hmcts.ccd.domain.service.validate.OperationContext; import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation; import uk.gov.hmcts.ccd.v2.external.resource.CaseDataResource; @@ -60,7 +61,8 @@ class CaseDataValidatorControllerTest { @BeforeEach void setUp() { MockitoAnnotations.initMocks(this); - when(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, EVENT_DATA)).thenReturn(DATA); + when(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, EVENT_DATA))) + .thenReturn(DATA); when(midEventCallback.invoke(CASE_TYPE_ID, EVENT_DATA, PAGE_ID)).thenReturn(DATA_NODE); } @@ -83,10 +85,10 @@ void shouldPassIfCaseDataValid() { @Test @DisplayName("should propagate exception") void shouldPropagateExceptionWhenThrown() { - when(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, EVENT_DATA)) + when(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, EVENT_DATA, null))) .thenThrow(RuntimeException.class); - assertThrows(Exception.class, () -> caseDataValidatorController.validate(CASE_TYPE_ID, PAGE_ID, + assertThrows(Exception.class, () -> caseDataValidatorController.validate(CASE_TYPE_ID, null, EVENT_DATA)); } } From 0a4408946ef1d1d995087fcf4a225709cc480c0c Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Mon, 15 Apr 2024 20:02:36 +0100 Subject: [PATCH 2/5] add case creation support --- .../AuthorisedValidateCaseFieldsOperation.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java index 1076fd8971..4714207a82 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import uk.gov.hmcts.ccd.config.JacksonUtils; @@ -61,7 +62,12 @@ public Map validateCaseDetails(OperationContext operationConte content.setData(JacksonUtils.convertValue(data)); - verifyReadAccess(caseTypeId, content); + String caseReference = content.getCaseReference(); + Set accessProfiles = StringUtils.isNotEmpty(caseReference)? + caseAccessService.getAccessProfilesByCaseReference(caseReference) : + caseAccessService.getCaseCreationRoles(caseTypeId); + + verifyReadAccess(caseTypeId, content, accessProfiles); return content.getData(); } @@ -72,15 +78,12 @@ public void validateData(Map data, CaseTypeDefinition caseType validateCaseFieldsOperation.validateData(data, caseTypeDefinition, content); } - private void verifyReadAccess(final String caseTypeId, CaseDataContent content) { + private void verifyReadAccess(final String caseTypeId, CaseDataContent content, Set accessProfiles) { final CaseTypeDefinition caseTypeDefinition = getCaseDefinitionType(caseTypeId); - Set caseAccessProfiles = - caseAccessService.getAccessProfilesByCaseReference(content.getCaseReference()); - if (!accessControlService.canAccessCaseTypeWithCriteria( caseTypeDefinition, - caseAccessProfiles, + accessProfiles, CAN_READ)) { content.setData(newHashMap()); return; @@ -90,7 +93,7 @@ private void verifyReadAccess(final String caseTypeId, CaseDataContent content) accessControlService.filterCaseFieldsByAccess( JacksonUtils.convertValueJsonNode(content.getData().get(JacksonUtils.DATA)), caseTypeDefinition.getCaseFieldDefinitions(), - caseAccessProfiles, + accessProfiles, CAN_READ, false))); } From 462c063def7659bc83ba5681e94ec3faf35c5804 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Mon, 15 Apr 2024 20:49:44 +0100 Subject: [PATCH 3/5] add case creation support and fix functional tests --- .../F-1015 - Start Case Event TTL/F-1015.feature | 4 ++-- .../F-1017.feature | 14 ++++---------- .../S-1017.21.td.json | 8 +------- .../S-1017.22.td.json | 8 +------- .../S-1017.23.td.json | 8 +------- .../S-1017.25.td.json | 7 +------ .../AuthorisedValidateCaseFieldsOperation.java | 4 ++-- 7 files changed, 12 insertions(+), 41 deletions(-) diff --git a/src/aat/resources/features/F-1015 - Start Case Event TTL/F-1015.feature b/src/aat/resources/features/F-1015 - Start Case Event TTL/F-1015.feature index 36b3e615f0..395f23e8a4 100644 --- a/src/aat/resources/features/F-1015 - Start Case Event TTL/F-1015.feature +++ b/src/aat/resources/features/F-1015 - Start Case Event TTL/F-1015.feature @@ -24,7 +24,7 @@ Feature: F-1015: Update Case - Start Case Event - Update Code for TTL And the response has all other details as expected And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] - @S-1015.1.repeat + @S-1015.1.repeat Scenario: TTLIncrement is set to "20" for the Case Event and Start Event is invoked on v1_external#/case-details-endpoint/startEventForCaseworkerUsingGET Given a user with [an active profile in CCD], And a successful call [to create a case] as in [F-1015_CreateCasePreRequisiteCaseworker_noTTL] @@ -461,7 +461,7 @@ Feature: F-1015: Update Case - Start Case Event - Update Code for TTL And the request [has a TTLIncrement of 20 days configured] And the request [is configured to trigger an About to Start callback that changes TTL.OverrideTTL] And it is submitted to call the [Start event creation process to update a case (v1_int caseworker)] operation of [CCD Data Store] - + Then a negative response is received And the response has all other details as expected And the response [contains the error message indicating unauthorised change to the TTL values] diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature index a1d379f3ee..902c721326 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature @@ -270,9 +270,7 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] - And the response [contains the TTL.OverrideTTL from the previouse data] - And the response [does not contain the TTL.Suspended as removed by callback (null -> missing)] + And the response [does not contain the TTL as citizen user has no access] @S-1017.22 #CCD-3535 Scenario: Trigger a mid event callback that changes TTL.Suspended (No -> NO). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 @@ -293,9 +291,7 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] - And the response [does not contain the TTL.OverrideTTL as removed by callback (null -> missing)] - And the response [contains the adjusted TTL.Suspended from the callback (No -> NO)] + And the response [does not contain the TTL as citizen user has no access] @S-1017.23 #CCD-3535 Scenario: Trigger a mid event callback that changes TTL.Suspended (Yes -> YES). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 @@ -316,9 +312,7 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] - And the response [does not contain the TTL.OverrideTTL as removed by callback (null -> missing)] - And the response [contains the adjusted TTL.Suspended from the callback (Yes -> YES)] + And the response [does not contain the TTL as citizen user has no access] @S-1017.25 #CCD-3562 @@ -335,7 +329,7 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] + And the response [does not contain the TTL as citizen user has no access] @S-1017.26 #CCD-3562 Scenario: Trigger a mid event callback that changes TTL set to null. Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json index b02c39551b..88a14dfafd 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json @@ -18,9 +18,7 @@ "has a TTLIncrement of 20 days configured", "is configured to trigger a mid event callback that changes the TTL.Suspended value (null -> missing)", - "contains the TTL.SystemTTL for the case, that has been set to 20 days from today", - "contains the TTL.OverrideTTL from the previouse data", - "does not contain the TTL.Suspended as removed by callback (null -> missing)" + "does not contain the TTL as citizen user has no access" ], "users": { @@ -62,10 +60,6 @@ "expectedResponse": { "body": { "data": { - "TTL": { - "SystemTTL": "${[scenarioContext][customValues][dateTwentyDaysFromToday]}", - "OverrideTTL": "${[scenarioContext][customValues][dateGreaterThanTTLGuardDate]}" - }, "TextField" : "[[ANYTHING_PRESENT]]" } } diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json index 23c642cfc6..a671b657f4 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json @@ -19,9 +19,7 @@ "is configured to trigger a mid event callback that changes the TTL.OverrideTTL value (null -> missing)", "is configured to trigger a mid event callback that changes the TTL.Suspended value (No -> NO)", - "contains the TTL.SystemTTL for the case, that has been set to 20 days from today", - "does not contain the TTL.OverrideTTL as removed by callback (null -> missing)", - "contains the adjusted TTL.Suspended from the callback (No -> NO)" + "does not contain the TTL as citizen user has no access" ], "users": { @@ -63,10 +61,6 @@ "expectedResponse": { "body": { "data": { - "TTL": { - "SystemTTL": "${[scenarioContext][customValues][dateTwentyDaysFromToday]}", - "Suspended": "NO" - }, "TextField" : "[[ANYTHING_PRESENT]]" } } diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json index ff1d41dbed..2e9c7ef098 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json @@ -19,9 +19,7 @@ "is configured to trigger a mid event callback that changes the TTL.OverrideTTL value (null -> missing)", "is configured to trigger a mid event callback that changes the TTL.Suspended value (Yes -> YES)", - "contains the TTL.SystemTTL for the case, that has been set to 20 days from today", - "does not contain the TTL.OverrideTTL as removed by callback (null -> missing)", - "contains the adjusted TTL.Suspended from the callback (Yes -> YES)" + "does not contain the TTL as citizen user has no access" ], "users": { @@ -63,10 +61,6 @@ "expectedResponse": { "body": { "data": { - "TTL": { - "SystemTTL": "${[scenarioContext][customValues][dateTwentyDaysFromToday]}", - "Suspended": "YES" - }, "TextField" : "[[ANYTHING_PRESENT]]" } } diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json index cfac479cbb..4b0f82c6a5 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json @@ -16,7 +16,7 @@ "has a TTLIncrement of 20 days configured", "is configured to trigger a mid event callback that responds with TTL missing", - "contains the TTL.SystemTTL for the case, that has been set to 20 days from today" + "does not contain the TTL as citizen user has no access" ], "users": { @@ -52,11 +52,6 @@ "expectedResponse": { "body": { "data": { - "TTL" : { - "SystemTTL" : "${[scenarioContext][customValues][dateTwentyDaysFromToday]}", - "OverrideTTL" : null, - "Suspended" : "No" - }, "TextField" : "[[ANYTHING_PRESENT]]" } } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java index 4714207a82..2e13f11963 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java @@ -63,8 +63,8 @@ public Map validateCaseDetails(OperationContext operationConte content.setData(JacksonUtils.convertValue(data)); String caseReference = content.getCaseReference(); - Set accessProfiles = StringUtils.isNotEmpty(caseReference)? - caseAccessService.getAccessProfilesByCaseReference(caseReference) : + Set accessProfiles = StringUtils.isNotEmpty(caseReference) + ? caseAccessService.getAccessProfilesByCaseReference(caseReference) : caseAccessService.getCaseCreationRoles(caseTypeId); verifyReadAccess(caseTypeId, content, accessProfiles); From 5884397e1d7a77ae089f6edfbb724b180ca41548 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Thu, 2 May 2024 14:44:47 +0100 Subject: [PATCH 4/5] implement Classified to /validate endpoints --- build.gradle | 1 + .../F-1017.feature | 22 +- .../S-1017.21.td.json | 4 +- .../S-1017.22.td.json | 4 +- .../S-1017.23.td.json | 4 +- .../S-1017.25.td.json | 2 +- .../uk/gov/hmcts/ccd/config/JacksonUtils.java | 4 +- .../service/createevent/MidEventCallback.java | 19 +- ...AuthorisedValidateCaseFieldsOperation.java | 22 +- ...ClassifiedValidateCaseFieldsOperation.java | 127 +++++++++++ .../validate/ValidateCaseFieldsOperation.java | 2 +- .../hmcts/ccd/config/JacksonUtilsTest.java | 30 +++ .../createevent/MidEventCallbackTest.java | 100 +++++---- ...orisedValidateCaseFieldsOperationTest.java | 187 ++++++++++++++++ ...sifiedValidateCaseFieldsOperationTest.java | 204 ++++++++++++++++++ .../CaseDataValidatorControllerTest.java | 5 +- 16 files changed, 643 insertions(+), 94 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperation.java create mode 100644 src/test/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperationTest.java create mode 100644 src/test/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperationTest.java diff --git a/build.gradle b/build.gradle index 61289aed2c..b8c474e0c2 100644 --- a/build.gradle +++ b/build.gradle @@ -280,6 +280,7 @@ dependencies { testRuntimeOnly "org.junit.platform:junit-platform-commons:1.8.1" testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.6.0' testImplementation group:'org.mockito', name: 'mockito-junit-jupiter', version:'3.6.0' + testImplementation 'org.mockito:mockito-inline:4.11.0' testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: powermockVersion testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: powermockVersion testImplementation group: 'io.rest-assured', name: 'rest-assured', version: '4.3.0' diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature index 902c721326..0e4b167e2a 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/F-1017.feature @@ -252,7 +252,7 @@ Feature: F-1017: Validate Event to Update TTL # #CCD-3535 & #CCD-3562: Trigger a mid-event callback that makes permitted changes to the TTL values: v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 #------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - @S-1017.21 #CCD-3535 + @S-1017.21 @Ignore #CCD-3535 Scenario: Trigger a mid event callback that changes TTL.Suspended (null -> missing). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 Given a user with [an active profile in CCD] And a user with [a caseworker with an active profile in CCD] @@ -270,9 +270,11 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [does not contain the TTL as citizen user has no access] + And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] + And the response [contains the TTL.OverrideTTL from the previouse data] + And the response [does not contain the TTL.Suspended as removed by callback (null -> missing)] - @S-1017.22 #CCD-3535 + @S-1017.22 @Ignore #CCD-3535 Scenario: Trigger a mid event callback that changes TTL.Suspended (No -> NO). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 Given a user with [an active profile in CCD] And a user with [a caseworker with an active profile in CCD] @@ -291,9 +293,11 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [does not contain the TTL as citizen user has no access] + And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] + And the response [does not contain the TTL.OverrideTTL as removed by callback (null -> missing)] + And the response [contains the adjusted TTL.Suspended from the callback (No -> NO)] - @S-1017.23 #CCD-3535 + @S-1017.23 @Ignore #CCD-3535 Scenario: Trigger a mid event callback that changes TTL.Suspended (Yes -> YES). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 Given a user with [an active profile in CCD] And a user with [a caseworker with an active profile in CCD] @@ -312,10 +316,12 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [does not contain the TTL as citizen user has no access] + And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] + And the response [does not contain the TTL.OverrideTTL as removed by callback (null -> missing)] + And the response [contains the adjusted TTL.Suspended from the callback (Yes -> YES)] - @S-1017.25 #CCD-3562 + @S-1017.25 @Ignore #CCD-3562 Scenario: Trigger a mid event callback that has TTL missing. Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 Given a user with [an active profile in CCD] And a successful call [to create a case] as in [CreateCase_TTLCaseType_PreRequisiteCitizen] @@ -329,7 +335,7 @@ Feature: F-1017: Validate Event to Update TTL Then a positive response is received And the response has all other details as expected - And the response [does not contain the TTL as citizen user has no access] + And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today] @S-1017.26 #CCD-3562 Scenario: Trigger a mid event callback that changes TTL set to null. Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1 diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json index 88a14dfafd..d5e0c8c075 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.21.td.json @@ -18,7 +18,9 @@ "has a TTLIncrement of 20 days configured", "is configured to trigger a mid event callback that changes the TTL.Suspended value (null -> missing)", - "does not contain the TTL as citizen user has no access" + "contains the TTL.SystemTTL for the case, that has been set to 20 days from today", + "contains the TTL.OverrideTTL from the previouse data", + "does not contain the TTL.Suspended as removed by callback (null -> missing)" ], "users": { diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json index a671b657f4..cf4abe6f20 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.22.td.json @@ -19,7 +19,9 @@ "is configured to trigger a mid event callback that changes the TTL.OverrideTTL value (null -> missing)", "is configured to trigger a mid event callback that changes the TTL.Suspended value (No -> NO)", - "does not contain the TTL as citizen user has no access" + "contains the TTL.SystemTTL for the case, that has been set to 20 days from today", + "does not contain the TTL.OverrideTTL as removed by callback (null -> missing)", + "contains the adjusted TTL.Suspended from the callback (No -> NO)" ], "users": { diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json index 2e9c7ef098..c0269a3e24 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.23.td.json @@ -19,7 +19,9 @@ "is configured to trigger a mid event callback that changes the TTL.OverrideTTL value (null -> missing)", "is configured to trigger a mid event callback that changes the TTL.Suspended value (Yes -> YES)", - "does not contain the TTL as citizen user has no access" + "contains the TTL.SystemTTL for the case, that has been set to 20 days from today", + "does not contain the TTL.OverrideTTL as removed by callback (null -> missing)", + "contains the adjusted TTL.Suspended from the callback (Yes -> YES)" ], "users": { diff --git a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json index 4b0f82c6a5..4bdba93c08 100644 --- a/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json +++ b/src/aat/resources/features/F-1017 - Validate Event to Update TTL/S-1017.25.td.json @@ -16,7 +16,7 @@ "has a TTLIncrement of 20 days configured", "is configured to trigger a mid event callback that responds with TTL missing", - "does not contain the TTL as citizen user has no access" + "contains the TTL.SystemTTL for the case, that has been set to 20 days from today" ], "users": { diff --git a/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java b/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java index 9a4d3bc03f..dd40582473 100644 --- a/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java +++ b/src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java @@ -202,10 +202,8 @@ private static String getValue(@NonNull JsonNode jsonNode) { } public static Map convertValueInDataField(Object from) { - Map map = MAPPER.convertValue(from, new TypeReference>() {}); - Map dataNode = new HashMap<>(); - dataNode.put(DATA, MAPPER.valueToTree(map)); + dataNode.put(DATA, MAPPER.valueToTree(convertValue(from))); return dataNode; } } 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..471dabe283 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 @@ -1,8 +1,7 @@ package uk.gov.hmcts.ccd.domain.service.createevent; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; + import java.util.Map; import java.util.Optional; import java.util.Set; @@ -52,9 +51,7 @@ public MidEventCallback(CallbackInvoker callbackInvoker, } @Transactional - public JsonNode invoke(String caseTypeId, - CaseDataContent content, - String pageId) { + public Map invoke(String caseTypeId, CaseDataContent content, String pageId) { if (!isBlank(pageId)) { Event event = content.getEvent(); final CaseTypeDefinition caseTypeDefinition = getCaseType(caseTypeId); @@ -91,11 +88,10 @@ public JsonNode invoke(String caseTypeId, caseDetailsBefore, currentOrNewCaseDetails, content.getIgnoreWarning()); - - return dataJsonNode(caseDetailsFromMidEventCallback.getData()); + return caseDetailsFromMidEventCallback.getData(); } } - return dataJsonNode(content.getData()); + return content.getData(); } private void removeNextPageFieldData(CaseDetails currentCaseDetails, Integer order, @@ -112,13 +108,6 @@ private void removeNextPageFieldData(CaseDetails currentCaseDetails, Integer ord } } - private JsonNode dataJsonNode(Map data) { - ObjectMapper mapper = new ObjectMapper(); - ObjectNode objectNode = mapper.createObjectNode(); - objectNode.set("data", mapper.valueToTree(data)); - return objectNode; - } - private CaseTypeDefinition getCaseType(String caseTypeId) { final CaseTypeDefinition caseTypeDefinition = caseDefinitionRepository.getCaseType(caseTypeId); if (caseTypeDefinition == null) { diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java index 2e13f11963..1926ac57d4 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperation.java @@ -13,7 +13,6 @@ import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; import uk.gov.hmcts.ccd.domain.service.common.AccessControlService; import uk.gov.hmcts.ccd.domain.service.common.CaseAccessService; -import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; import java.util.Map; @@ -32,20 +31,17 @@ public class AuthorisedValidateCaseFieldsOperation implements ValidateCaseFields private final CaseDefinitionRepository caseDefinitionRepository; private final CaseAccessService caseAccessService; private final ValidateCaseFieldsOperation validateCaseFieldsOperation; - private final MidEventCallback midEventCallback; public AuthorisedValidateCaseFieldsOperation(AccessControlService accessControlService, @Qualifier(CachedCaseDefinitionRepository.QUALIFIER) CaseDefinitionRepository caseDefinitionRepository, CaseAccessService caseAccessService, - @Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) - ValidateCaseFieldsOperation validateCaseFieldsOperation, - MidEventCallback midEventCallback) { + @Qualifier(ClassifiedValidateCaseFieldsOperation.QUALIFIER) + ValidateCaseFieldsOperation validateCaseFieldsOperation) { this.accessControlService = accessControlService; this.caseDefinitionRepository = caseDefinitionRepository; this.caseAccessService = caseAccessService; this.validateCaseFieldsOperation = validateCaseFieldsOperation; - this.midEventCallback = midEventCallback; } @Override @@ -54,13 +50,6 @@ public Map validateCaseDetails(OperationContext operationConte CaseDataContent content = operationContext.content(); String caseTypeId = operationContext.caseTypeId(); - String pageId = operationContext.pageId(); - - final JsonNode data = midEventCallback.invoke(caseTypeId, - content, - pageId); - - content.setData(JacksonUtils.convertValue(data)); String caseReference = content.getCaseReference(); Set accessProfiles = StringUtils.isNotEmpty(caseReference) @@ -81,6 +70,11 @@ public void validateData(Map data, CaseTypeDefinition caseType private void verifyReadAccess(final String caseTypeId, CaseDataContent content, Set accessProfiles) { final CaseTypeDefinition caseTypeDefinition = getCaseDefinitionType(caseTypeId); + if (content.getData() == null) { + content.setData(newHashMap()); + return; + } + if (!accessControlService.canAccessCaseTypeWithCriteria( caseTypeDefinition, accessProfiles, @@ -91,7 +85,7 @@ private void verifyReadAccess(final String caseTypeId, CaseDataContent content, content.setData(JacksonUtils.convertValueInDataField( accessControlService.filterCaseFieldsByAccess( - JacksonUtils.convertValueJsonNode(content.getData().get(JacksonUtils.DATA)), + JacksonUtils.convertValueJsonNode(content.getData()), caseTypeDefinition.getCaseFieldDefinitions(), accessProfiles, CAN_READ, diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperation.java new file mode 100644 index 0000000000..49c7eca96c --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperation.java @@ -0,0 +1,127 @@ +package uk.gov.hmcts.ccd.domain.service.validate; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.data.definition.CachedCaseDefinitionRepository; +import uk.gov.hmcts.ccd.data.definition.CaseDefinitionRepository; +import uk.gov.hmcts.ccd.domain.model.definition.CaseDetails; +import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition; +import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; +import uk.gov.hmcts.ccd.domain.service.common.CaseDataService; +import uk.gov.hmcts.ccd.domain.service.common.CaseService; +import uk.gov.hmcts.ccd.domain.service.common.SecurityClassificationServiceImpl; +import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; +import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; + +import java.util.Map; + +import static com.google.common.collect.Maps.newHashMap; +import static java.util.Optional.ofNullable; + +@Service +@Qualifier(ClassifiedValidateCaseFieldsOperation.QUALIFIER) +public class ClassifiedValidateCaseFieldsOperation implements ValidateCaseFieldsOperation { + public static final String QUALIFIER = "classified"; + + private final ValidateCaseFieldsOperation validateCaseFieldsOperation; + private final SecurityClassificationServiceImpl classificationService; + private final MidEventCallback midEventCallback; + private final CaseDataService caseDataService; + private final CaseDefinitionRepository caseDefinitionRepository; + private final CaseService caseService; + + + + public ClassifiedValidateCaseFieldsOperation(@Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER) + ValidateCaseFieldsOperation validateCaseFieldsOperation, + SecurityClassificationServiceImpl classificationService, + MidEventCallback midEventCallback, CaseDataService caseDataService, + @Qualifier(CachedCaseDefinitionRepository.QUALIFIER) + CaseDefinitionRepository caseDefinitionRepository, + CaseService caseService) { + this.validateCaseFieldsOperation = validateCaseFieldsOperation; + this.classificationService = classificationService; + this.midEventCallback = midEventCallback; + this.caseDataService = caseDataService; + this.caseDefinitionRepository = caseDefinitionRepository; + this.caseService = caseService; + } + + @Override + public Map validateCaseDetails(OperationContext operationContext) { + validateCaseFieldsOperation.validateCaseDetails(operationContext); + + CaseDataContent content = operationContext.content(); + String caseTypeId = operationContext.caseTypeId(); + String pageId = operationContext.pageId(); + + + final Map callbackData = midEventCallback.invoke(caseTypeId, + content, + pageId); + + CaseTypeDefinition caseTypeDefinition = getCaseDefinitionType(caseTypeId); + + CaseGenerationResult caseDetailsAndIsCreate = generateOrRetrieveCaseDetails(operationContext, + caseTypeDefinition, callbackData); + + CaseDetails classifiedCaseDetails = + classificationService.applyClassification(caseDetailsAndIsCreate.caseDetails(), + caseDetailsAndIsCreate.isCreate()).orElse(new CaseDetails()); + + content.setData(classifiedCaseDetails.getData()); + + return content.getData(); + } + + @Override + public void validateData(Map data, CaseTypeDefinition caseTypeDefinition, + CaseDataContent content) { + validateCaseFieldsOperation.validateData(data, caseTypeDefinition, content); + } + + private CaseGenerationResult generateOrRetrieveCaseDetails(OperationContext operationContext, + CaseTypeDefinition caseTypeDefinition, + Map callbackData) { + CaseDetails caseDetails; + boolean isCreate; + if (StringUtils.isNotEmpty(operationContext.content().getCaseReference())) { + isCreate = false; + caseDetails = + caseService.getCaseDetails(caseTypeDefinition.getJurisdictionId(), + operationContext.content().getCaseReference()); + } else { + isCreate = true; + caseDetails = new CaseDetails(); + caseDetails.setCaseTypeId(operationContext.caseTypeId()); + caseDetails.setSecurityClassification(caseTypeDefinition.getSecurityClassification()); + } + + caseDetails.setData(callbackData); + deduceDataClassificationForNewFields(caseTypeDefinition, caseDetails); + + return new CaseGenerationResult(caseDetails, isCreate); + } + + private CaseTypeDefinition getCaseDefinitionType(String caseTypeId) { + final CaseTypeDefinition caseTypeDefinition = caseDefinitionRepository.getCaseType(caseTypeId); + if (caseTypeDefinition == null) { + throw new ValidationException("Cannot find case type definition for " + caseTypeId); + } + return caseTypeDefinition; + } + + private void deduceDataClassificationForNewFields(CaseTypeDefinition caseTypeDefinition, CaseDetails caseDetails) { + Map defaultSecurityClassifications = caseDataService.getDefaultSecurityClassifications( + caseTypeDefinition, + caseDetails.getData(), + ofNullable(caseDetails.getDataClassification()).orElse( + newHashMap())); + caseDetails.setDataClassification(defaultSecurityClassifications); + } + + private record CaseGenerationResult(CaseDetails caseDetails, boolean isCreate) {} + +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java index 41e8fe44b0..8302388db4 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/validate/ValidateCaseFieldsOperation.java @@ -8,7 +8,7 @@ public interface ValidateCaseFieldsOperation { - Map validateCaseDetails(OperationContext validationContext); + Map validateCaseDetails(OperationContext operationContext); void validateData(final Map data, final CaseTypeDefinition caseTypeDefinition, diff --git a/src/test/java/uk/gov/hmcts/ccd/config/JacksonUtilsTest.java b/src/test/java/uk/gov/hmcts/ccd/config/JacksonUtilsTest.java index df8f50b9e1..e912728180 100644 --- a/src/test/java/uk/gov/hmcts/ccd/config/JacksonUtilsTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/config/JacksonUtilsTest.java @@ -17,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static uk.gov.hmcts.ccd.config.JacksonUtils.DATA; import static uk.gov.hmcts.ccd.config.JacksonUtils.MAPPER; class JacksonUtilsTest { @@ -405,4 +406,33 @@ static Map organisationPolicyDefaultValue(String role) result.put("OrganisationPolicyField", data); return result; } + + @Test + void testConvertValueInDataField() throws JsonProcessingException { + JsonNode jsonNode = MAPPER.readTree(""" + { + "Name": "Name_1", + "Class": [ + { + "id": "6da7a0cf-8186-49d4-813d-c299d8f3491b", + "value": { + "ClassName": "Class_1" + } + }, + { + "id": "b7662626-b640-48bb-8afa-9fa78dcbd2ec", + "value": { + "ClassName": "Class_2" + } + } + ], + "Number": null + } + """); + + Map result = JacksonUtils.convertValueInDataField(jsonNode); + + assertEquals(1, result.size()); + assertEquals(jsonNode, result.get(DATA)); + } } diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallbackTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallbackTest.java index f4c12f0e18..dd234dcbbf 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallbackTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/MidEventCallbackTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.google.common.collect.Lists; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -31,8 +32,8 @@ import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker; import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.never; @@ -131,10 +132,11 @@ void shouldInvokeMidEventCallbackWhenUrlDefined() { void shouldUpdateCaseDetailsFromMidEventCallback() throws Exception { final Map data = JacksonUtils.convertValue(MAPPER.readTree( - "{\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\"\n" - + "}")); + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name" + }""")); CaseDetails updatedCaseDetails = caseDetails(data); CaseDataContent content = newCaseDataContent().withEvent(event).withData(data) .withIgnoreWarning(IGNORE_WARNINGS) @@ -150,27 +152,27 @@ void shouldUpdateCaseDetailsFromMidEventCallback() throws Exception { given(caseService.createNewCaseDetails(CASE_TYPE_ID, JURISDICTION_ID, data)).willReturn(caseDetails); - JsonNode result = midEventCallback.invoke(CASE_TYPE_ID, + Map result = midEventCallback.invoke(CASE_TYPE_ID, content, "createCase1"); - final JsonNode expectedResponse = MAPPER.readTree( - "{" - + "\"data\": {\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\"\n" - + "}}"); + Map expectedResponse = JacksonUtils.convertValue((MAPPER.readTree( + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name" + }"""))); assertThat(result, is(expectedResponse)); } @Test @DisplayName("test no interaction when pageId not present") void testNoInteractionWhenMidEventCallbackUrlNotPresent() throws IOException { - JsonNode result = midEventCallback.invoke(CASE_TYPE_ID, + Map result = midEventCallback.invoke(CASE_TYPE_ID, newCaseDataContent().withEvent(event).withData(data).withIgnoreWarning(IGNORE_WARNINGS).build(), ""); - final JsonNode expectedResponse = MAPPER.readTree("{\"data\": {}}"); + final Map expectedResponse = JacksonUtils.convertValue((MAPPER.readTree("{}"))); assertThat("Data should stay unchanged", result, is(expectedResponse)); verifyNoMoreInteractions(callbackInvoker, caseDefinitionRepository, eventTriggerService, uiDefinitionRepository, caseService); @@ -181,10 +183,11 @@ void testNoInteractionWhenMidEventCallbackUrlNotPresent() throws IOException { void shouldPassEventDataToMidEventCallback() throws Exception { Map eventData = JacksonUtils.convertValue((MAPPER.readTree( - "{\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\"\n" - + "}"))); + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name" + }"""))); CaseDetails updatedCaseDetails = caseDetails(eventData); CaseDataContent content = newCaseDataContent() @@ -203,16 +206,16 @@ void shouldPassEventDataToMidEventCallback() throws Exception { when(caseService.createNewCaseDetails(CASE_TYPE_ID, JURISDICTION_ID, eventData)).thenReturn(caseDetails); given(caseService.populateCurrentCaseDetailsWithEventFields(content, updatedCaseDetails)).willReturn(null); - JsonNode result = midEventCallback.invoke(CASE_TYPE_ID, + Map result = midEventCallback.invoke(CASE_TYPE_ID, content, "createCase1"); - JsonNode expectedResponse = MAPPER.readTree( - "{" - + "\"data\": {\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\"\n" - + "}}"); + Map expectedResponse = JacksonUtils.convertValue((MAPPER.readTree( + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name" + }"""))); assertAll( () -> assertThat(result, is(expectedResponse)), @@ -231,22 +234,25 @@ void shouldPassEventDataToMidEventCallback() throws Exception { void shouldContainAllDataFromExistingCaseReferenceDuringAMidEventCallback() throws Exception { Map eventData = JacksonUtils.convertValue((MAPPER.readTree( - "{\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\"\n" - + "}"))); + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name" + }"""))); Map existingData = JacksonUtils.convertValue((MAPPER.readTree( - "{\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonMiddleName\": \"Middle Name\"\n" - + "}"))); + """ + { + "PersonFirstName": "First Name", + "PersonMiddleName": "Middle Name" + }"""))); Map combineData = JacksonUtils.convertValue((MAPPER.readTree( - "{\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\",\n" - + " \"PersonMiddleName\": \"Middle Name\"\n" - + "}"))); + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name", + "PersonMiddleName": "Middle Name" + }"""))); CaseDetails existingCaseDetails = caseDetails(existingData); CaseDetails combineCaseDetails = caseDetails(combineData); CaseDataContent content = newCaseDataContent() @@ -271,17 +277,17 @@ void shouldContainAllDataFromExistingCaseReferenceDuringAMidEventCallback() thro .willReturn(combineCaseDetails); - JsonNode result = midEventCallback.invoke(CASE_TYPE_ID, + Map result = midEventCallback.invoke(CASE_TYPE_ID, content, "createCase1"); - JsonNode expectedResponse = MAPPER.readTree( - "{" - + "\"data\": {\n" - + " \"PersonFirstName\": \"First Name\",\n" - + " \"PersonLastName\": \"Last Name\",\n" - + " \"PersonMiddleName\": \"Middle Name\"\n" - + "}}"); + Map expectedResponse = JacksonUtils.convertValue((MAPPER.readTree( + """ + { + "PersonFirstName": "First Name", + "PersonLastName": "Last Name", + "PersonMiddleName": "Middle Name" + }"""))); assertAll( () -> assertThat(result, is(expectedResponse)), @@ -296,7 +302,7 @@ void shouldContainAllDataFromExistingCaseReferenceDuringAMidEventCallback() thro @DisplayName("should call filter case data content when wizard page order exists") void shouldCallFilterCaseDataContentWhenWizardPageOrderExists() { given(uiDefinitionRepository.getWizardPageCollection(CASE_TYPE_ID, event.getEventId())) - .willReturn(asList(wizardPageWithCallback)); + .willReturn(Collections.singletonList(wizardPageWithCallback)); CaseDetails existingCaseDetails = caseDetails(data); when(caseService.getCaseDetails(caseDetails.getJurisdiction(), CASE_REFERENCE)) .thenReturn(existingCaseDetails); diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperationTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperationTest.java new file mode 100644 index 0000000000..805f05c275 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/AuthorisedValidateCaseFieldsOperationTest.java @@ -0,0 +1,187 @@ +package uk.gov.hmcts.ccd.domain.service.validate; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import uk.gov.hmcts.ccd.config.JacksonUtils; +import uk.gov.hmcts.ccd.data.definition.CaseDefinitionRepository; +import uk.gov.hmcts.ccd.domain.model.casedataaccesscontrol.AccessProfile; +import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition; +import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; +import uk.gov.hmcts.ccd.domain.service.common.AccessControlService; +import uk.gov.hmcts.ccd.domain.service.common.CaseAccessService; +import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.config.JacksonUtils.DATA; + + +class AuthorisedValidateCaseFieldsOperationTest { + private static final JsonNodeFactory JSON_NODE_FACTORY = new JsonNodeFactory(false); + private static final String CASE_TYPE_ID = "GrantOnly"; + private static final String PAGE_ID = "1"; + private static final String USER_ROLE_1 = "user-role-1"; + private static final String CASE_REFERENCE = "1234123412341234"; + + + @Mock + private AccessControlService accessControlService; + + @Mock + private CaseDefinitionRepository caseDefinitionRepository; + + @Mock + private CaseAccessService caseAccessService; + + @Mock + private ValidateCaseFieldsOperation validateCaseFieldsOperation; + + @InjectMocks + private AuthorisedValidateCaseFieldsOperation authorisedValidateCaseFieldsOperation; + + AutoCloseable openMocks; + + @BeforeEach + void setUp() { + openMocks = MockitoAnnotations.openMocks(this); + + CaseTypeDefinition caseTypeDefinition = new CaseTypeDefinition(); + when(caseDefinitionRepository.getCaseType(anyString())).thenReturn(caseTypeDefinition); + } + + @Test + @DisplayName("should Return Empty CaseDetails With No Access Profile") + void shouldReturnEmptyCaseDetailsWithNoAccessProfile() { + OperationContext operationContext = Mockito.mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + content.setCaseReference(CASE_REFERENCE); + content.setData(JacksonUtils.convertValueInDataField(new ObjectNode(null))); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + when(operationContext.pageId()).thenReturn(PAGE_ID); + + when(caseAccessService.getAccessProfilesByCaseReference(anyString())).thenReturn(Set.of()); + + when(caseAccessService.getAccessProfilesByCaseReference(anyString())).thenReturn(Set.of()); + + Map result = authorisedValidateCaseFieldsOperation.validateCaseDetails(operationContext); + + assertAll( + () -> verify(validateCaseFieldsOperation).validateCaseDetails(operationContext), + () -> verify(caseAccessService).getAccessProfilesByCaseReference(CASE_REFERENCE), + () -> verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID), + () -> assertNotNull(result), + () -> assertTrue(result.isEmpty()) + ); + } + + @Test + @DisplayName("should Return CaseDetails With Access Profile") + void shouldReturnCaseDetailsWithAccessProfile() { + OperationContext operationContext = Mockito.mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + content.setCaseReference(CASE_REFERENCE); + + content.setData(JacksonUtils.convertValue(new ObjectNode(null))); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + when(operationContext.pageId()).thenReturn(PAGE_ID); + + when(caseAccessService.getAccessProfilesByCaseReference(anyString())).thenReturn(Set.of()); + + Set accessProfiles = Set.of(AccessProfile.builder().accessProfile(USER_ROLE_1).build()); + when(caseAccessService.getAccessProfilesByCaseReference(anyString())).thenReturn(accessProfiles); + + when(accessControlService.canAccessCaseTypeWithCriteria(any(), any(), any())) + .thenReturn(true); + + ObjectNode filteredData = new ObjectNode(JSON_NODE_FACTORY); + filteredData.put("filtered_field1", "filtered_value1"); + filteredData.put("filtered_field2", "filtered_value2"); + when(accessControlService.filterCaseFieldsByAccess(any(), any(), any(), any(), anyBoolean())) + .thenReturn(filteredData); + + Map result = authorisedValidateCaseFieldsOperation.validateCaseDetails(operationContext); + + assertAll( + () -> verify(validateCaseFieldsOperation).validateCaseDetails(operationContext), + () -> verify(caseAccessService).getAccessProfilesByCaseReference(CASE_REFERENCE), + () -> verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID), + () -> assertNotNull(result), + () -> assertTrue(result.containsKey(DATA)), + () -> assertEquals(2, result.get(DATA).size()) + ); + } + + @Test + @DisplayName("should Apply CaseCreationRoles When Case Not Found") + void shouldApplyCaseCreationRolesWhenCaseNotFound() { + OperationContext operationContext = Mockito.mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + content.setCaseReference(""); + content.setData(JacksonUtils.convertValueInDataField(new ObjectNode(null))); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + when(operationContext.pageId()).thenReturn(PAGE_ID); + + when(caseAccessService.getAccessProfilesByCaseReference(anyString())).thenReturn(Set.of()); + + authorisedValidateCaseFieldsOperation.validateCaseDetails(operationContext); + + assertAll( + () -> verify(validateCaseFieldsOperation).validateCaseDetails(operationContext), + () -> verify(caseAccessService).getCaseCreationRoles(CASE_TYPE_ID), + () -> verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID) + ); + } + + @Test + void shouldGetCaseDefinitionTypeThrowsException() { + when(caseDefinitionRepository.getCaseType(anyString())).thenReturn(null); + + OperationContext operationContext = Mockito.mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + + assertThrows(ValidationException.class, + () -> authorisedValidateCaseFieldsOperation.validateCaseDetails(operationContext)); + } + + @Test + void shouldValidateData() { + Map data = new HashMap<>(); + CaseTypeDefinition caseTypeDefinition = new CaseTypeDefinition(); + CaseDataContent content = new CaseDataContent(); + + authorisedValidateCaseFieldsOperation.validateData(data, caseTypeDefinition, content); + + verify(validateCaseFieldsOperation).validateData(data, caseTypeDefinition, content); + } + + @AfterEach + void tearDown() throws Exception { + openMocks.close(); + } +} diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperationTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperationTest.java new file mode 100644 index 0000000000..f345ad21a3 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/validate/ClassifiedValidateCaseFieldsOperationTest.java @@ -0,0 +1,204 @@ +package uk.gov.hmcts.ccd.domain.service.validate; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import uk.gov.hmcts.ccd.config.JacksonUtils; +import uk.gov.hmcts.ccd.data.definition.CaseDefinitionRepository; +import uk.gov.hmcts.ccd.domain.model.definition.CaseDetails; +import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition; +import uk.gov.hmcts.ccd.domain.model.definition.JurisdictionDefinition; +import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent; +import uk.gov.hmcts.ccd.domain.service.common.CaseDataService; +import uk.gov.hmcts.ccd.domain.service.common.CaseService; +import uk.gov.hmcts.ccd.domain.service.common.SecurityClassificationServiceImpl; +import uk.gov.hmcts.ccd.domain.service.createevent.MidEventCallback; +import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class ClassifiedValidateCaseFieldsOperationTest { + private static final String CASE_TYPE_ID = "GrantOnly"; + private static final String PAGE_ID = "1"; + private static final String CASE_REFERENCE = "1234123412341234"; + private static final String JURISDICTION_ID = "Probate"; + private static final CaseTypeDefinition caseTypeDefinition = getCaseTypeDefinition(); + private static final Map EMPTY_CASE_DATA = Collections.emptyMap(); + private static final Map EMPTY_CASE_DATA_CLASSIFICATION = Collections.emptyMap(); + private static final CaseDetails EMPTY_CASE_DETAILS = new CaseDetails(); + + @Mock + private CaseDefinitionRepository caseDefinitionRepository; + + @Mock + private ValidateCaseFieldsOperation validateCaseFieldsOperation; + + @Mock + private MidEventCallback midEventCallback; + + @Mock + private CaseService caseService; + + @Mock + private CaseDataService caseDataService; + + @Mock + private SecurityClassificationServiceImpl classificationService; + + @InjectMocks + private ClassifiedValidateCaseFieldsOperation classifiedValidateCaseFieldsOperation; + + AutoCloseable openMocks; + + @BeforeEach + void setUp() { + openMocks = MockitoAnnotations.openMocks(this); + + when(caseDefinitionRepository.getCaseType(anyString())).thenReturn(caseTypeDefinition); + } + + + @Test + @DisplayName("should Return Empty CaseDetails with Existing Case") + void shouldReturnEmptyCaseDetailsWithExistingCase() { + OperationContext operationContext = mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + content.setCaseReference(CASE_REFERENCE); + content.setData(JacksonUtils.convertValueInDataField(new ObjectNode(null))); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + when(operationContext.pageId()).thenReturn(PAGE_ID); + + when(midEventCallback.invoke(anyString(), any(), any())).thenReturn(Collections.emptyMap()); + when(caseService.getCaseDetails(anyString(), anyString())).thenReturn(EMPTY_CASE_DETAILS); + when(caseDataService.getDefaultSecurityClassifications(any(), anyMap(), anyMap())) + .thenReturn(Collections.emptyMap()); + when(classificationService.applyClassification(any(), anyBoolean())).thenReturn(Optional.empty()); + + Map result = classifiedValidateCaseFieldsOperation.validateCaseDetails(operationContext); + + assertAll( + () -> verify(validateCaseFieldsOperation).validateCaseDetails(operationContext), + () -> verify(midEventCallback).invoke(CASE_TYPE_ID, content, PAGE_ID), + () -> verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID), + () -> verify(caseService).getCaseDetails(JURISDICTION_ID, CASE_REFERENCE), + () -> verify(caseDataService).getDefaultSecurityClassifications(caseTypeDefinition, EMPTY_CASE_DATA, + EMPTY_CASE_DATA_CLASSIFICATION), + () -> verify(classificationService).applyClassification(EMPTY_CASE_DETAILS, false), + () -> assertNull(result) + ); + } + + @Test + @DisplayName("should Return Empty CaseDetails with Create Case") + void shouldReturnEmptyCaseDetailsWithCreateCase() { + CaseDetails caseDetails = new CaseDetails(); + caseDetails.setCaseTypeId(CASE_TYPE_ID); + caseDetails.setData(Collections.emptyMap()); + caseDetails.setDataClassification(Collections.emptyMap()); + OperationContext operationContext = mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + content.setData(JacksonUtils.convertValueInDataField(new ObjectNode(null))); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + when(operationContext.pageId()).thenReturn(PAGE_ID); + + when(midEventCallback.invoke(anyString(), any(), any())).thenReturn(Collections.emptyMap()); + when(caseDataService.getDefaultSecurityClassifications(any(), anyMap(), anyMap())) + .thenReturn(Collections.emptyMap()); + when(classificationService.applyClassification(any(), anyBoolean())).thenReturn(Optional.empty()); + + Map result = classifiedValidateCaseFieldsOperation.validateCaseDetails(operationContext); + + ArgumentCaptor argumentCaseDetail = ArgumentCaptor.forClass(CaseDetails.class); + + assertAll( + () -> verify(validateCaseFieldsOperation).validateCaseDetails(operationContext), + () -> verify(midEventCallback).invoke(CASE_TYPE_ID, content, PAGE_ID), + () -> verify(caseDefinitionRepository).getCaseType(CASE_TYPE_ID), + () -> verify(caseService, never()).getCaseDetails(JURISDICTION_ID, CASE_REFERENCE), + () -> verify(caseDataService).getDefaultSecurityClassifications(caseTypeDefinition, EMPTY_CASE_DATA, + EMPTY_CASE_DATA_CLASSIFICATION), + () -> verify(classificationService).applyClassification(argumentCaseDetail.capture(), + ArgumentMatchers.eq(true)), + () -> assertEquals(CASE_TYPE_ID, argumentCaseDetail.getValue().getCaseTypeId()), + () -> assertNull(result) + ); + } + + @Test + void shouldGetCaseDefinitionTypeThrowsException() { + when(caseDefinitionRepository.getCaseType(anyString())).thenReturn(null); + + OperationContext operationContext = mock(OperationContext.class); + CaseDataContent content = new CaseDataContent(); + when(operationContext.content()).thenReturn(content); + when(operationContext.caseTypeId()).thenReturn(CASE_TYPE_ID); + + assertThrows(ValidationException.class, + () -> classifiedValidateCaseFieldsOperation.validateCaseDetails(operationContext)); + } + + @Test + void shouldValidateData() { + Map data = new HashMap<>(); + CaseTypeDefinition caseTypeDefinition = new CaseTypeDefinition(); + CaseDataContent content = new CaseDataContent(); + + classifiedValidateCaseFieldsOperation.validateData(data, caseTypeDefinition, content); + + verify(validateCaseFieldsOperation).validateData(data, caseTypeDefinition, content); + } + + @Test + void shouldThrowValidateException() { + Map data = new HashMap<>(); + CaseDataContent content = new CaseDataContent(); + + doThrow(new ValidationException("Validation failed")).when(validateCaseFieldsOperation) + .validateData(any(), any(), any()); + + ValidationException exception = assertThrows(ValidationException.class, () -> + classifiedValidateCaseFieldsOperation.validateData(data, caseTypeDefinition, content)); + assertEquals("Validation failed", exception.getMessage()); + } + + private static @NotNull CaseTypeDefinition getCaseTypeDefinition() { + JurisdictionDefinition jurisdictionDefinition = new JurisdictionDefinition(); + jurisdictionDefinition.setId(JURISDICTION_ID); + CaseTypeDefinition caseTypeDefinition = new CaseTypeDefinition(); + caseTypeDefinition.setJurisdictionDefinition(jurisdictionDefinition); + return caseTypeDefinition; + } + + @AfterEach + void tearDown() throws Exception { + openMocks.close(); + } +} diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java b/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java index 84490d4113..d8ac244dab 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/CaseDataValidatorControllerTest.java @@ -20,6 +20,7 @@ import uk.gov.hmcts.ccd.v2.external.resource.CaseDataResource; import java.util.Map; +import java.util.Objects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -63,7 +64,7 @@ void setUp() { MockitoAnnotations.initMocks(this); when(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, EVENT_DATA))) .thenReturn(DATA); - when(midEventCallback.invoke(CASE_TYPE_ID, EVENT_DATA, PAGE_ID)).thenReturn(DATA_NODE); + when(midEventCallback.invoke(CASE_TYPE_ID, EVENT_DATA, PAGE_ID)).thenReturn(DATA); } @Nested @@ -78,7 +79,7 @@ void shouldPassIfCaseDataValid() { assertAll( () -> assertThat(response.getStatusCode(), is(HttpStatus.OK)), - () -> assertThat(response.getBody().getData(), is(UNWRAPPED_DATA_NODE)) + () -> assertThat(Objects.requireNonNull(response.getBody()).getData(), is(UNWRAPPED_DATA_NODE)) ); } From 4b247db66cea8f9a99f8d4cc1388acbd3c682d2e Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Wed, 12 Mar 2025 15:34:29 +0000 Subject: [PATCH 5/5] fix unit test --- .../createcase/DefaultCreateCaseOperationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java index 7deaa640cd..02e5df62e5 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/DefaultCreateCaseOperationTest.java @@ -388,9 +388,9 @@ void shouldUpdateCaseDetailsWithTtlIncrement() { given(eventTriggerService.isPreStateValid(null, eventTrigger)).willReturn(Boolean.TRUE); given(savedCaseType.getState()).willReturn(caseEventStateId); given(caseTypeService.findState(CASE_TYPE, caseEventStateId)).willReturn(caseEventState); - given(validateCaseFieldsOperation.validateCaseDetails(CASE_TYPE_ID, eventData)).willReturn(data); + given(validateCaseFieldsOperation.validateCaseDetails(new OperationContext(CASE_TYPE_ID, eventData))).willReturn(data); given(caseSanitiser.sanitise(eq(CASE_TYPE), anyMap())).willReturn(data); - + // SETUP TTL CaseFieldDefinition ttlDefinition = new CaseFieldDefinition(); ttlDefinition.setId("TTL"); @@ -407,7 +407,7 @@ public Map answer(InvocationOnMock invocation) throws Throwabl } }); given(applicationParams.getTtlGuard()).willReturn(2); - + given(submitCaseTransaction.submitCase(same(event), same(CASE_TYPE), same(IDAM_USER), @@ -419,7 +419,7 @@ public Map answer(InvocationOnMock invocation) throws Throwabl public CaseDetails answer(InvocationOnMock invocation) throws Throwable { CaseDetails caseDetails = (CaseDetails) invocation.getArguments()[4]; return caseDetails; - } + } }); CaseDetails returnedCaseDetails = defaultCreateCaseOperation.createCaseDetails(CASE_TYPE_ID,