From 4279d0541394542f0d571a173d817f571e8a1259 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Wed, 4 Dec 2024 17:51:51 +0000 Subject: [PATCH 1/6] Implement permission and value checks to prevent unnecessary overwrites in recursive field processing --- .../common/RestrictedFieldProcessor.java | 237 + .../createevent/CreateCaseEventService.java | 14 +- .../common/RestrictedFieldProcessorTest.java | 4183 +++++++++++++++++ .../CreateCaseEventServiceTest.java | 3 + 4 files changed, 4434 insertions(+), 3 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java create mode 100644 src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java new file mode 100644 index 0000000000..4e799e7891 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java @@ -0,0 +1,237 @@ +package uk.gov.hmcts.ccd.domain.service.common; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.domain.model.casedataaccesscontrol.AccessProfile; +import uk.gov.hmcts.ccd.domain.model.definition.AccessControlList; +import uk.gov.hmcts.ccd.domain.model.definition.CaseFieldDefinition; +import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition; +import uk.gov.hmcts.ccd.domain.model.definition.FieldTypeDefinition; +import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.StreamSupport; + +import static uk.gov.hmcts.ccd.domain.service.common.AccessControlService.extractAccessProfileNames; + +@Slf4j +@Service +public class RestrictedFieldProcessor { + + private final CaseAccessService caseAccessService; + + public RestrictedFieldProcessor(CaseAccessService caseAccessService) { + this.caseAccessService = caseAccessService; + } + + public Map filterRestrictedFields(final CaseTypeDefinition caseTypeDefinition, + final Map sanitisedData, + final Map existingData, + final String caseReference) { + Set accessProfiles = caseAccessService.getAccessProfilesByCaseReference(caseReference); + if (accessProfiles == null || accessProfiles.isEmpty()) { + throw new ValidationException("Cannot find user roles for the user"); + } + + final Set accessProfileNames = extractAccessProfileNames(accessProfiles); + final Map mergedData = new HashMap<>(sanitisedData); + + sanitisedData.forEach((key, sanitizedValue) -> { + JsonNode existingValue = existingData.get(key); + + if (existingValue != null) { + CaseFieldDefinition rootFieldDefinition = caseTypeDefinition.getCaseFieldDefinitions() + .stream() + .filter(field -> field.getId().equals(key)) + .findFirst() + .orElse(null); + + if (rootFieldDefinition != null && rootFieldDefinition.isCompoundFieldType()) { + JsonNode updatedValue = processSubFieldsRecursively( + rootFieldDefinition, + sanitizedValue, + existingValue, + accessProfileNames + ); + + mergedData.put(key, updatedValue); + } + } + }); + + return mergedData; + } + + private JsonNode processSubFieldsRecursively(CaseFieldDefinition parentFieldDefinition, + JsonNode sanitizedNode, + JsonNode existingNode, + Set accessProfileNames) { + if (existingNode == null) { + return sanitizedNode; + } + + if (existingNode.isArray()) { + return processCollectionFields(parentFieldDefinition, sanitizedNode, existingNode, accessProfileNames); + } + + if (!existingNode.isObject()) { + return sanitizedNode; + } + + ObjectNode sanitizedObjectNode = sanitizedNode != null && sanitizedNode.isObject() + ? (ObjectNode) sanitizedNode.deepCopy() + : JsonNodeFactory.instance.objectNode(); + + ObjectNode existingObjectNode = (ObjectNode) existingNode; + + existingObjectNode.fieldNames().forEachRemaining(fieldName -> { + JsonNode existingSubField = existingObjectNode.get(fieldName); + JsonNode sanitizedSubField = sanitizedObjectNode.get(fieldName); + + CaseFieldDefinition subFieldDefinition = getFieldDefinition(fieldName, parentFieldDefinition); + + if (sanitizedSubField == null) { + log.debug("Missing field '{}' under '{}'.", fieldName, parentFieldDefinition.getId()); + + if (isCreateWithoutReadAllowed(subFieldDefinition.getAccessControlLists(), accessProfileNames)) { + log.info("Adding missing field '{}' under '{}'.", fieldName, parentFieldDefinition.getId()); + sanitizedObjectNode.set(fieldName, existingSubField); + } + } else { + sanitizedObjectNode.set(fieldName, processSubFieldsRecursively( + subFieldDefinition, + sanitizedSubField, + existingSubField, + accessProfileNames)); + } + }); + + return sanitizedObjectNode; + } + + private JsonNode processCollectionFields(CaseFieldDefinition subFieldDefinition, + JsonNode sanitizedArrayNode, + JsonNode existingArrayNode, + Set accessProfileNames) { + + ArrayNode sanitizedArray = sanitizedArrayNode != null && sanitizedArrayNode.isArray() + ? (ArrayNode) sanitizedArrayNode.deepCopy() + : JsonNodeFactory.instance.arrayNode(); + + ArrayNode existingArray = (ArrayNode) existingArrayNode; + + for (JsonNode existingItem : existingArray) { + JsonNode existingItemId = existingItem.get("id"); + + Optional matchingNewItem = StreamSupport.stream(sanitizedArray.spliterator(), false) + .filter(newItem -> !isNullId(newItem) && newItem.get("id").equals(existingItemId)) + .findFirst(); + + if (matchingNewItem.isEmpty()) { + log.debug("Missing collection item with ID '{}' under '{}'.", existingItemId, + subFieldDefinition.getId()); + + if (isCreateWithoutReadAllowed(subFieldDefinition.getAccessControlLists(), accessProfileNames)) { + log.info("Adding missing collection item with ID '{}' under '{}'.", existingItemId, + subFieldDefinition.getId()); + sanitizedArray.add(existingItem); + } + } else { + JsonNode newValueField = matchingNewItem.get().get("value"); + JsonNode existingValueField = existingItem.get("value"); + + if (existingValueField != null) { + JsonNode processedValueField; + + if (existingValueField.isObject()) { + processedValueField = processSubFieldsRecursively(subFieldDefinition, + newValueField, + existingValueField, + accessProfileNames); + } else { + processedValueField = processSimpleValueField( + subFieldDefinition, newValueField, existingValueField, accessProfileNames); + } + + ((ObjectNode) matchingNewItem.get()).set("value", processedValueField); + } + } + } + + return sanitizedArray; + } + + private JsonNode processSimpleValueField(CaseFieldDefinition subFieldDefinition, JsonNode newValueField, + JsonNode existingValueField, + Set accessProfileNames) { + if (newValueField == null) { + log.debug("Missing value field under '{}'.", subFieldDefinition.getId()); + + if (isCreateWithoutReadAllowed(subFieldDefinition.getAccessControlLists(), accessProfileNames)) { + log.info("Adding missing value field under '{}'.", subFieldDefinition.getId()); + return existingValueField; + } + } + + return newValueField != null ? newValueField : existingValueField; + } + + private boolean isNullId(JsonNode newItem) { + return newItem.get("id") == null + || newItem.get("id").equals(NullNode.getInstance()) + || "null".equalsIgnoreCase(newItem.get("id").asText()); + } + + private boolean isCreateWithoutReadAllowed(List fieldAccessControlLists, + Set accessProfileNames) { + boolean hasReadPermission = fieldAccessControlLists + .stream() + .anyMatch(acl -> accessProfileNames.contains(acl.getAccessProfile()) + && Boolean.TRUE.equals(acl.isRead())); + + boolean hasCreatePermission = fieldAccessControlLists + .stream() + .anyMatch(acl -> accessProfileNames.contains(acl.getAccessProfile()) + && Boolean.TRUE.equals(acl.isCreate())); + + return !hasReadPermission && hasCreatePermission; + } + + private CaseFieldDefinition getFieldDefinition(String fieldName, + CaseFieldDefinition parentFieldDefinition) { + // Check if the parent field's definition contains subfields + FieldTypeDefinition parentFieldType = parentFieldDefinition.getFieldTypeDefinition(); + + if (parentFieldType == null) { + return parentFieldDefinition; + } + + if (parentFieldType.getComplexFields() != null && !parentFieldType.getComplexFields().isEmpty()) { + return parentFieldType.getComplexFields() + .stream() + .filter(subField -> subField.getId().equals(fieldName)) + .findFirst() + .orElse(parentFieldDefinition); + } + + if (parentFieldType.getCollectionFieldTypeDefinition() != null && !parentFieldType + .getCollectionFieldTypeDefinition().getComplexFields().isEmpty()) { + return parentFieldType.getCollectionFieldTypeDefinition().getComplexFields() + .stream() + .filter(subField -> subField.getId().equals(fieldName)) + .findFirst() + .orElse(parentFieldDefinition); + } + + return parentFieldDefinition; + } +} 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 b50299525a..367cedd3e1 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 @@ -33,6 +33,7 @@ import uk.gov.hmcts.ccd.domain.service.common.CaseService; 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.common.RestrictedFieldProcessor; import uk.gov.hmcts.ccd.domain.service.common.SecurityClassificationServiceImpl; import uk.gov.hmcts.ccd.domain.service.common.UIDService; import uk.gov.hmcts.ccd.domain.service.getcasedocument.CaseDocumentService; @@ -97,6 +98,7 @@ public class CreateCaseEventService { private final CaseDocumentTimestampService caseDocumentTimestampService; private final ApplicationParams applicationParams; private final CaseAccessGroupUtils caseAccessGroupUtils; + private final RestrictedFieldProcessor restrictedFieldProcessor; @Inject public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final UserRepository userRepository, @@ -130,7 +132,8 @@ public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final U final CaseLinkService caseLinkService, final ApplicationParams applicationParams, final CaseAccessGroupUtils caseAccessGroupUtils, - final CaseDocumentTimestampService caseDocumentTimestampService) { + final CaseDocumentTimestampService caseDocumentTimestampService, + final RestrictedFieldProcessor restrictedFieldProcessor) { this.userRepository = userRepository; this.caseDetailsRepository = caseDetailsRepository; @@ -161,7 +164,7 @@ public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final U this.applicationParams = applicationParams; this.caseAccessGroupUtils = caseAccessGroupUtils; this.caseDocumentTimestampService = caseDocumentTimestampService; - + this.restrictedFieldProcessor = restrictedFieldProcessor; } @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -436,7 +439,12 @@ CaseDetails mergeUpdatedFieldsToCaseDetails(final Map data, final Map sanitisedData = caseSanitiser.sanitise(caseTypeDefinition, nonNullData); final Map caseData = new HashMap<>(Optional.ofNullable(caseDetails.getData()) .orElse(emptyMap())); - caseData.putAll(sanitisedData); + + final Map filteredData = + restrictedFieldProcessor.filterRestrictedFields(caseTypeDefinition, sanitisedData, caseData, + caseDetails.getReferenceAsString()); + + caseData.putAll(filteredData); clonedCaseDetails.setData(globalSearchProcessorService.populateGlobalSearchData(caseTypeDefinition, caseData)); diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java new file mode 100644 index 0000000000..4614fb3332 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java @@ -0,0 +1,4183 @@ +package uk.gov.hmcts.ccd.domain.service.common; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.slf4j.LoggerFactory; +import uk.gov.hmcts.ccd.domain.model.definition.AccessControlList; +import uk.gov.hmcts.ccd.domain.model.definition.CaseFieldDefinition; +import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.domain.model.definition.FieldTypeDefinition.COLLECTION; +import static uk.gov.hmcts.ccd.domain.model.definition.FieldTypeDefinition.COMPLEX; +import static uk.gov.hmcts.ccd.domain.model.definition.FieldTypeDefinition.DOCUMENT; +import static uk.gov.hmcts.ccd.domain.service.common.AccessControlServiceTest.ACCESS_PROFILES; +import static uk.gov.hmcts.ccd.domain.service.common.AccessControlServiceTest.getTagFieldDefinition; +import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.AccessControlListBuilder.anAcl; +import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.CaseFieldBuilder.newCaseField; +import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.CaseTypeBuilder.newCaseType; +import static uk.gov.hmcts.ccd.domain.service.common.TestBuildersUtil.FieldTypeBuilder.aFieldType; + +class RestrictedFieldProcessorTest { + + static String complexTypeArrayPayload = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + static String arrayPayload = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + static String complexTypePayload = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + static String nestedComplexTypeArrayPayload = """ + { + "caseCategory": { + "value": { + "code": "Test", + "label": "Test" + }, + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + + @Mock + private CaseAccessService caseAccessService; + + private Logger logger; + private ListAppender listAppender; + private List loggingEventList; + private RestrictedFieldProcessor service; + private AutoCloseable openMocks; + + @BeforeEach + void setUp() { + openMocks = MockitoAnnotations.openMocks(this); + + service = new RestrictedFieldProcessor(caseAccessService); + when(caseAccessService.getAccessProfilesByCaseReference(anyString())).thenReturn(ACCESS_PROFILES); + } + + private CaseFieldDefinition noteWithoutCreateAndReadPermission() { + CaseFieldDefinition note = newCaseField() + .withId("Note") + .withFieldType(aFieldType() + .withId("NoteComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("Txt") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(getTagFieldDefinition()) + .build()) + .build(); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(note).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return note; + } + + private CaseFieldDefinition noteWithCreatePermissionWithoutReadPermission() { + AccessControlList deletePermission = new AccessControlList(); + deletePermission.setAccessProfile("caseworker-probate-loa1"); + deletePermission.setCreate(true); + deletePermission.setRead(false); + + CaseFieldDefinition note = newCaseField() + .withId("Note") + .withAcl(deletePermission) + .withFieldType(aFieldType() + .withId("NoteComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("Txt") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(getTagFieldDefinition()) + .build()) + .build(); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(note).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return note; + } + + private CaseFieldDefinition tagWithoutCreateAndReadPermission() { + CaseFieldDefinition tags = getTagFieldDefinition(); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(tags).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return tags; + } + + private CaseFieldDefinition tagsWithCreatePermissionWithoutReadPermission() { + AccessControlList controlList = new AccessControlList(); + controlList.setAccessProfile("caseworker-probate-loa1"); + controlList.setCreate(true); + controlList.setRead(false); + + CaseFieldDefinition tags = getTagFieldDefinition(); + tags.setAccessControlLists(List.of(controlList)); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(tags).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return tags; + } + + private CaseFieldDefinition noteWithNestedFieldsWithoutCreateAndReadPermission() { + CaseFieldDefinition note = newCaseField() + .withId("Note") + .withFieldType(aFieldType() + .withId("NoteComplex") + .withType(COMPLEX) + + .withComplexField(newCaseField() + .withId("type") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("metadata") + .withFieldType(aFieldType() + .withId("MetadataComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("authorName") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("creationDate") + .withFieldType(aFieldType() + .withId("Date") + .withType("Date") + .build()) + .build()) + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("content") + .withFieldType(aFieldType() + .withId("ContentComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("title") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("body") + .withFieldType(aFieldType() + .withId("TextArea") + .withType("TextArea") + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("additionalInfo") + .withFieldType(aFieldType() + .withId("AdditionalInfoComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("noteID") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("category") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("tags") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .build()) + .build()) + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("location") + .withFieldType(aFieldType() + .withId("AddressComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("AddressLine1") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("AddressLine2") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("City") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("State") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("Country") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("PostalCode") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .build()) + .build()) + .build()) + .build(); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(note).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return note; + } + + private CaseFieldDefinition noteWithNestedFieldsWithCreateAndWithoutReadPermission() { + AccessControlList controlList = new AccessControlList(); + controlList.setAccessProfile("caseworker-probate-loa1"); + controlList.setCreate(true); + controlList.setRead(false); + + CaseFieldDefinition note = noteWithNestedFieldsWithoutCreateAndReadPermission(); + note.setAccessControlLists(List.of(controlList)); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(note).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return note; + } + + private CaseFieldDefinition caseCategoryFieldWithoutCreateAndReadPermission() { + CaseFieldDefinition caseCategory = newCaseField() + .withId("caseCategory") + .withFieldType(aFieldType() + .withId("CaseCategoryComplex") + .withType(COMPLEX) + + .withComplexField(newCaseField() + .withId("value") + .withFieldType(aFieldType() + .withId("ValueComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("code") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("label") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("list_items") + .withFieldType(aFieldType() + .withId("ListItemsCollection") + .withType(COLLECTION) + .withCollectionFieldType(aFieldType() + .withId("ListItemComplex") + .withType(COMPLEX) + + .withComplexField(newCaseField() + .withId("id") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("value") + .withFieldType(aFieldType() + .withId("ValueComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("code") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("label") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .build()) + .build()) + .build()) + .build()) + .build()) + .build()) + .build(); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(caseCategory).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return caseCategory; + } + + private CaseFieldDefinition caseCategoryFieldWithCreateWithoutReadPermission() { + AccessControlList accessControlList = new AccessControlList(); + accessControlList.setAccessProfile("caseworker-probate-loa1"); + accessControlList.setCreate(true); + accessControlList.setRead(false); + + CaseFieldDefinition caseCategory = caseCategoryFieldWithoutCreateAndReadPermission(); + caseCategory.setAccessControlLists(List.of(accessControlList)); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(caseCategory).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return caseCategory; + } + + private CaseFieldDefinition generatedCaseDocumentsFieldWithoutCreateAndReadPermission() { + CaseFieldDefinition generatedCaseDocuments = newCaseField() + .withId("generatedCaseDocuments") + .withFieldType(aFieldType() + .withId("DocumentCollection") + .withType(COLLECTION) + .withCollectionFieldType(aFieldType() + .withId("DocumentComplex") + .withType(COMPLEX) + .withComplexField(newCaseField() + .withId("createdBy") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + + .withComplexField(newCaseField() + .withId("documentLink") + .withFieldType(aFieldType() + .withId("DocumentLinkComplex") + .withType(COMPLEX) + .build()) + .build()) + .withComplexField(newCaseField() + .withId("documentName") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("documentSize") + .withFieldType(aFieldType() + .withId("Number") + .withType("Number") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("documentType") + .withFieldType(aFieldType() + .withId("Text") + .withType("Text") + .build()) + .build()) + .withComplexField(newCaseField() + .withId("createdDatetime") + .withFieldType(aFieldType() + .withId("DateTime") + .withType("DateTime") + .build()) + .build()) + .build()) + .build()) + .build(); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(generatedCaseDocuments).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return generatedCaseDocuments; + } + + private CaseFieldDefinition generatedCaseDocumentsFieldWithCreateWithoutReadPermission() { + AccessControlList accessControlList = new AccessControlList(); + accessControlList.setAccessProfile("caseworker-probate-loa1"); + accessControlList.setCreate(true); + accessControlList.setRead(false); + + CaseFieldDefinition document = generatedCaseDocumentsFieldWithoutCreateAndReadPermission(); + document.setAccessControlLists(List.of(accessControlList)); + + final CaseTypeDefinition caseTypeDefinition = newCaseType().withField(document).build(); + caseTypeDefinition.getCaseFieldDefinitions().forEach(CaseFieldDefinition::propagateACLsToNestedFields); + + return document; + } + + @Test + void shouldAddMissingNullFieldToComplexTypeWhenCreateWithoutReadPermission() { + final String nestedComplexTypeArrayPayload = """ + { + "caseCategory": { + "value": { + "code": null, + "label": "Test" + }, + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + final String newDataString = """ + { + "caseCategory": { + "value": { + "label": "Test" + }, + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'code' under 'value'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithCreateWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertEquals("Test", result.get("caseCategory").get("value").get("label").asText()), + () -> assertEquals("null", result.get("caseCategory").get("value").get("code").asText()), + () -> assertEquals("Test", + result.get("caseCategory").get("list_items").get(0).get("value").get("label").asText()), + () -> assertEquals("Test", + result.get("caseCategory").get("list_items").get(0).get("value").get("code").asText()) + ); + } + + @Test + void shouldKeepWhenNewNullFieldAddedToComplexTypeWhenCreateWithoutReadPermission() { + final String nestedComplexTypeArrayPayload = """ + { + "caseCategory": { + "value": { + "label": null + }, + "list_items": [ + { + "id": "123456", + "value": { + "code": null, + "label": null + } + } + ] + } + } + """; + + final String newDataString = """ + { + "caseCategory": { + "value": { + "code": null, + "label": "Test" + }, + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(caseCategoryFieldWithCreateWithoutReadPermission()).build(); + Map existingData = getJsonMapNode(nestedComplexTypeArrayPayload); + Map newData = getJsonMapNode(newDataString); + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, newData, + existingData, "123"); + + assertAll( + () -> assertEquals("Test", filteredFields.get("caseCategory").get("value").get("label").asText()), + () -> assertEquals("null", filteredFields.get("caseCategory").get("value").get("code").asText()), + () -> assertEquals("Test", + filteredFields.get("caseCategory").get("list_items").get(0).get("value").get("label").asText()), + () -> assertEquals("Test", + filteredFields.get("caseCategory").get("list_items").get(0).get("value").get("code").asText()) + ); + } + + @Test + void shouldAddMissingValueComplexFieldToComplexTypeWhenCreateWithoutReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'value' under 'caseCategory'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithCreateWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertTrue(result.get("caseCategory").get("value").has("code")), + () -> assertTrue(result.get("caseCategory").get("value").has("label")), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("code").asText()), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("label").asText()) + ); + } + + @Test + void shouldAddMissingValueSubFieldsToComplexTypeWhenCreateWithoutReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "value": null, + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'code' under 'value'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithCreateWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertTrue(result.get("caseCategory").get("value").has("code")), + () -> assertTrue(result.get("caseCategory").get("value").has("label")), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("code").asText()), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("label").asText()) + ); + } + + @Test + void shouldAddMissingListItemsArrayToComplexTypeWhenCreateWithoutReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "value": { + "code": "Test", + "label": "Test" + } + } + } + """; + + String expectedMessage = "Adding missing field 'list_items' under 'caseCategory'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithCreateWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertTrue(result.get("caseCategory").has("list_items")), + () -> assertEquals(1, result.get("caseCategory").get("list_items").size()) + ); + } + + @Test + void shouldAddMissingListItemsNodeToComplexTypeWhenCreateWithoutReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "value": { + "code": "Test", + "label": "Test" + }, + "list_items": [ + { + "id": "444", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"123456\"' under 'list_items'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithCreateWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertTrue(result.get("caseCategory").has("list_items")), + () -> assertEquals(2, result.get("caseCategory").get("list_items").size()) + ); + } + + @Test + void shouldDoNothingForMissingValueComplexFieldToComplexTypeWhenWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Missing field 'value' under 'caseCategory'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("list_items")), + () -> assertFalse(result.get("caseCategory").has("value")) + ); + } + + @Test + void shouldDoNothingForNullValueWhenWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "value": null, + "list_items": [ + { + "id": "123456", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Missing field 'label' under 'value'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("list_items")), + () -> assertEquals(1, result.get("caseCategory").get("list_items").size()), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertTrue(result.get("caseCategory").get("value").asText().isEmpty()) + ); + } + + @Test + void shouldDoNothingForMissingListItemArrayWhenWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "value": { + "code": "Test", + "label": "Test" + } + } + } + """; + + String expectedMessage = "Missing field 'list_items' under 'caseCategory'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("label").asText()), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("code").asText()), + () -> assertFalse(result.get("caseCategory").has("list_items")) + ); + } + + @Test + void shouldDoNothingForMissingListItemNodeWhenWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "caseCategory": { + "value": { + "code": "Test", + "label": "Test" + }, + "list_items": [ + { + "id": "444", + "value": { + "code": "Test", + "label": "Test" + } + } + ] + } + } + """; + + String expectedMessage = "Missing collection item with ID '\"123456\"' under 'list_items'."; + + Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + caseCategoryFieldWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("caseCategory")), + () -> assertTrue(result.get("caseCategory").has("value")), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("label").asText()), + () -> assertEquals("Test", result.get("caseCategory").get("value").get("code").asText()), + () -> assertTrue(result.get("caseCategory").has("list_items")), + () -> assertEquals(1, result.get("caseCategory").get("list_items").size()), + () -> assertEquals("444", result.get("caseCategory").get("list_items").get(0).get("id").asText()) + ); + } + + @Test + void shouldAddMissingCategoryFieldToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'Category' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertNotNull(result.get("Note").get("Tags").get(0).get("value").get("Category")), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertEquals("public", result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()), + () -> assertEquals("public", result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Note").get("Tags").get(1).get("value").get("Category").asText()) + ); + } + + @Test + void shouldDoNothingWhenCategoryFieldSetToNullWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": null + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithCreatePermissionWithoutReadPermission()).build(); + + Map existingData = getJsonMapNode(complexTypeArrayPayload); + Map newData = getJsonMapNode(newDataString); + + Map result = service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + assertAll( + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertNotNull(result.get("Note").get("Tags").get(0).get("value").get("Category")), + () -> assertEquals("null", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()) + ); + } + + @Test + void shouldAddMissingValueSubFieldsToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", + result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldAddNullValueToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": null, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'Category' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldAddFirstMissingNodeToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + } + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(3, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertFalse(result.get("Note").get("Tags").get(0).has("id")), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("private", result.get("Note").get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Note").get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddSecondMissingNodeToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + } + } + ] + } + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"456\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(3, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertFalse(result.get("Note").get("Tags").get(1).has("id")), + () -> assertTrue(result.get("Note").get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddMissingSubFirstFieldToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()) + ); + } + + @Test + void shouldAddFirstNullIDToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "null" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(3, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("null", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddSecondNullIDToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": null + } + ] + } + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"456\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(3, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("null", result.get("Note").get("Tags").get(1).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddMissingNodesToCollectionTypeWhenWithCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [] + } + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenCategoryFieldMissingWhenWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing field 'Category' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertFalse(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenValueMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").isEmpty()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenValueIsNullWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": null, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").isEmpty()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenNodeIDMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + } + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertFalse(result.get("Note").get("Tags").get(0).has("id")), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenSecondNodeIDMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + } + } + ] + } + } + """; + + String expectedMessage = "Missing collection item with ID '\"456\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertFalse(result.get("Note").get("Tags").get(1).has("id")) + ); + } + + @Test + void shouldDoNothingWhenSecondSubFieldMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertFalse(result.get("Note").get("Tags").get(1).get("value").has("Tag")), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenNodeIDNullStringWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "null" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("id")), + () -> assertEquals("null", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenNodeIDNullWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": null + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + } + """; + + String expectedMessage = "Missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertEquals(2, result.get("Note").get("Tags").size()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Note").get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", + result.get("Note").get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", + result.get("Note").get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(0).has("id")), + () -> assertEquals("null", result.get("Note").get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Note").get("Tags").get(1).has("value")), + () -> assertEquals("public", + result.get("Note").get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", + result.get("Note").get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Note").get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenNodesAreMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "Tags": [] + } + } + """; + + String expectedMessage = "Missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(complexTypeArrayPayload, newDataString, + noteWithoutCreateAndReadPermission(), + Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Note")), + () -> assertTrue(result.get("Note").has("Tags")), + () -> assertTrue(result.get("Note").get("Tags").isEmpty()) + ); + } + + @Test + void shouldAddMissingCollectionTypeNoeIDWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + } + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagsWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(3, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertFalse(result.get("Tags").get(0).has("id")), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()), + () -> assertTrue(result.get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddMissingSecondCollectionTypeNodeIDWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + } + } + ] + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"456\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagsWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(3, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertFalse(result.get("Tags").get(1).has("id")), + () -> assertTrue(result.get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddChangedCollectionTypeNodeIDWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "999" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Adding missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagsWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(3, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("999", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()), + () -> assertTrue(result.get("Tags").get(2).has("value")), + () -> assertTrue(result.get("Tags").get(2).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(2).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(2).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(2).get("value").get("Category").asText()), + () -> assertEquals("123", result.get("Tags").get(2).get("id").asText()) + ); + } + + @Test + void shouldAddMissingCollectionTypeSubFieldsWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagsWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldAddNullCollectionTypeValueWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": null, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagsWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldAddMissingCollectionTypeSecondNodeFieldWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagsWithCreatePermissionWithoutReadPermission(), + Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldReturnIfNoChangeInArrayNodeWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(tagWithoutCreateAndReadPermission()).build(); + Map existingData = getJsonMapNode(arrayPayload); + Map newData = getJsonMapNode(newDataString); + + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, newData, + existingData, "123"); + assertEquals(existingData, filteredFields); + + assertEquals(0, listAppender.list.size()); + } + + @Test + void shouldDoNothingWhenSecondCollectionTypeNodeIDMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + } + } + ] + } + """; + + String expectedMessage = "Missing collection item with ID '\"456\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertFalse(result.get("Tags").get(1).has("id")) + ); + } + + @Test + void shouldDoNothingWhenCollectionTypeNodeIDMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + } + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertFalse(result.get("Tags").get(0).has("id")), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenCollectionTypeNodeIDChangedWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "999" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Missing collection item with ID '\"123\"' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("999", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenCollectionTypeValueMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).get("value").isEmpty()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenCollectionTypeValueIsNullWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": null, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).get("value").isEmpty()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldDoNothingWhenCollectionTypeSubFieldMissingWithoutCreateWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + String expectedMessage = "Missing field 'Tag' under 'Tags'."; + + Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + tagWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.containsKey("Tags")), + () -> assertEquals(2, result.get("Tags").size()), + () -> assertTrue(result.get("Tags").get(0).has("value")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), + () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), + () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), + () -> assertTrue(result.get("Tags").get(0).has("id")), + () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), + () -> assertTrue(result.get("Tags").get(1).has("value")), + () -> assertFalse(result.get("Tags").get(1).get("value").has("Tag")), + () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), + () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), + () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + ); + } + + @Test + void shouldReturnIfNoChangeInArrayNodeWithCreatePermissionWithoutReadPermission() { + final String newDataString = """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(tagsWithCreatePermissionWithoutReadPermission()).build(); + + Map existingData = getJsonMapNode(arrayPayload); + Map newData = getJsonMapNode(newDataString); + + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, newData, + existingData, "123"); + assertEquals(existingData, filteredFields); + + assertEquals(0, listAppender.list.size()); + } + + @Test + void shouldAddMissingDocumentNodeToDocumentCollectionWithCreateWithoutRead() { + CaseFieldDefinition caseFieldDefinition = newCaseField() + .withId("Documents") + .withFieldType(aFieldType() + .withType(COLLECTION) + .withCollectionFieldType(aFieldType() + .withType(DOCUMENT) + .withId(DOCUMENT) + .build()) + .build()) + .withOrder(1) + .withAcl(anAcl() + .withRole("caseworker-probate-loa1") + .withCreate(true) + .withUpdate(false) + .withDelete(false) + .withRead(false) + .build()) + .build(); + Map newData = getJsonMapNode(""" + { + "Documents": [ + { + "id": "CollectionField2", + "value": { + "document_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943", + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + } + ] + }"""); + Map existingData = getJsonMapNode(""" + { + "Documents": [ + { + "id": "CollectionField1", + "value": { + "document_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg", + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + }, + { + "id": "CollectionField2", + "value": { + "document_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943", + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + } + ] + }"""); + + setupLogging().setLevel(Level.INFO); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(caseFieldDefinition).build(); + + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, newData, + existingData, "123"); + + assertAll( + () -> assertTrue(filteredFields.containsKey("Documents")), + () -> assertTrue(filteredFields.get("Documents").isArray()), + () -> assertEquals(2, filteredFields.get("Documents").size()), + () -> assertEquals("CollectionField2", filteredFields.get("Documents").get(0).get("id").asText()), + () -> assertTrue(filteredFields.get("Documents").get(0).has("value")), + () -> assertTrue(filteredFields.get("Documents").get(0).get("value").has("document_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75dd3943", + filteredFields.get("Documents").get(0).get("value").get("document_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(0).get("value").has("document_binary_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75dd3943/binary", + filteredFields.get("Documents").get(0).get("value").get("document_binary_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(0).get("value").has("document_filename")), + () -> assertEquals("Elastic Search test Case.png --> updated by Solicitor 1", + filteredFields.get("Documents").get(0).get("value").get("document_filename").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).has("id")), + () -> assertEquals("CollectionField1", filteredFields.get("Documents").get(1).get("id").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).has("value")), + () -> assertTrue(filteredFields.get("Documents").get(1).get("value").has("document_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg", + filteredFields.get("Documents").get(1).get("value").get("document_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).get("value").has("document_binary_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg/binary", + filteredFields.get("Documents").get(1).get("value").get("document_binary_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).get("value").has("document_filename")), + () -> assertEquals("Elastic Search test Case.png --> updated by Solicitor 1", + filteredFields.get("Documents").get(1).get("value").get("document_filename").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).has("id")) + ); + + String expectedMessage = "Adding missing collection item with ID '\"CollectionField1\"' under 'Documents'."; + + loggingEventList = listAppender.list; + assertAll( + () -> assertTrue(loggingEventList.stream().allMatch(log -> log.getLevel() == Level.INFO)), + () -> assertTrue(loggingEventList.stream().anyMatch(log -> + log.getFormattedMessage().equals(expectedMessage))) + ); + } + + @Test + void shouldDoNothingWhenMissingDocumentSubFieldWithoutCreateWithoutRead() { + CaseFieldDefinition caseFieldDefinition = newCaseField() + .withId("Documents") + .withFieldType(aFieldType() + .withType(COLLECTION) + .withCollectionFieldType(aFieldType() + .withType(DOCUMENT) + .withId(DOCUMENT) + .build()) + .build()) + .withOrder(1) + .withAcl(anAcl() + .withRole("caseworker-probate-loa1") + .withCreate(false) + .withUpdate(false) + .withDelete(false) + .withRead(false) + .build()) + .build(); + Map newData = getJsonMapNode(""" + { + "Documents": [ + { + "id": "CollectionField1", + "value": { + "document_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg", + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + }, + { + "id": "CollectionField2", + "value": { + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + } + ] + }"""); + Map existingData = getJsonMapNode(""" + { + "Documents": [ + { + "id": "CollectionField1", + "value": { + "document_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg", + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + }, + { + "id": "CollectionField2", + "value": { + "document_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943", + "document_binary_url": "{{DM_STORE_BASE_URL}}/documents/\ + ae5c9e4b-1385-483e-b1b7-607e75dd3943/binary", + "document_filename": "Elastic Search test Case.png --> updated by Solicitor 1" + } + } + ] + }"""); + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(caseFieldDefinition).build(); + + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, newData, + existingData, "123"); + assertAll( + () -> assertTrue(filteredFields.containsKey("Documents")), + () -> assertTrue(filteredFields.get("Documents").isArray()), + () -> assertEquals(2, filteredFields.get("Documents").size()), + () -> assertEquals("CollectionField1", filteredFields.get("Documents").get(0).get("id").asText()), + () -> assertTrue(filteredFields.get("Documents").get(0).has("value")), + () -> assertTrue(filteredFields.get("Documents").get(0).get("value").has("document_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg", + filteredFields.get("Documents").get(0).get("value").get("document_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(0).get("value").has("document_binary_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75yfhgfhg/binary", + filteredFields.get("Documents").get(0).get("value").get("document_binary_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(0).get("value").has("document_filename")), + () -> assertEquals("Elastic Search test Case.png --> updated by Solicitor 1", + filteredFields.get("Documents").get(0).get("value").get("document_filename").asText()), + () -> assertEquals("CollectionField2", filteredFields.get("Documents").get(1).get("id").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).has("value")), + () -> assertFalse(filteredFields.get("Documents").get(1).get("value").has("document_url")), + () -> assertTrue(filteredFields.get("Documents").get(1).get("value").has("document_binary_url")), + () -> assertEquals("{{DM_STORE_BASE_URL}}/documents/ae5c9e4b-1385-483e-b1b7-607e75dd3943/binary", + filteredFields.get("Documents").get(1).get("value").get("document_binary_url").asText()), + () -> assertTrue(filteredFields.get("Documents").get(1).get("value").has("document_filename")), + () -> assertEquals("Elastic Search test Case.png --> updated by Solicitor 1", + filteredFields.get("Documents").get(1).get("value").get("document_filename").asText()) + ); + + String expectedMessage = "Missing field 'document_url' under 'Documents'."; + + loggingEventList = listAppender.list; + assertAll( + () -> assertTrue(loggingEventList.stream().anyMatch(log -> + log.getLevel() == Level.DEBUG && log.getFormattedMessage().equals(expectedMessage)) + ) + ); + } + + @Test + void shouldNotPermitDeletionOfAddressLine2FieldInComplexNestedObjectWithoutDeletePermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": null, + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithoutCreateAndReadPermission()).build(); + var newDataNode = getJsonMapNode(newDataString); + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, + newDataNode, getJsonMapNode(complexTypePayload), "123"); + + assertEquals(newDataNode, filteredFields); + } + + @Test + void shouldAddMissingLocationFieldInComplexNestedObjectWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + } + } + } + """; + + String expectedMessage = "Adding missing field 'location' under 'Note'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content") + .get("additionalInfo").get("tags").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("123 Main Street", + result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")) + ); + } + + @Test + void shouldNotPermitNullOfAddressLine2FieldInComplexNestedObjectWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'AddressLine1' under 'location'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("123 Main Street", + result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + } + + @Test + void shouldAddMissingSubFieldInInnerComplexNestedObjectWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'noteID' under 'additionalInfo'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + + } + + @Test + void shouldDoNothingWhenSubFieldIsNullInInnerComplexNestedObjectWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": null + } + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithCreateAndWithoutReadPermission()).build(); + Map newDataNode = getJsonMapNode(newDataString); + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, + newDataNode, getJsonMapNode(complexTypePayload), "123"); + + assertEquals(newDataNode, filteredFields); + } + + @Test + void shouldAddMissingSubFieldsOfNullComplexFieldInInnerComplexNestedObjectWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": null + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'noteID' under 'additionalInfo'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + } + + @Test + void shouldAddMissingComplexFieldInInnerComplexNestedObjectWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables." + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'additionalInfo' under 'content'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + } + + @Test + void shouldAddMissingParentComplexFieldWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'content' under 'Note'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("123 Main Street", result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()) + ); + } + + @Test + void shouldAddNullParentComplexFieldWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": null, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'additionalInfo' under 'content'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("123 Main Street", + result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()) + ); + } + + @Test + void shouldAddMissingTopSimpleFieldInParentNodeWithCreateAndWithoutReadPermission() { + final String newDataString = """ + { + "Note": { + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Adding missing field 'type' under 'Note'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("123 Main Street", + result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), + () -> assertEquals("abc123", + result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), + () -> assertEquals("Meeting", + result.get("Note").get("content").get("additionalInfo").get("category").asText()), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), + () -> assertEquals("project, timeline, deliverable", + result.get("Note").get("content").get("additionalInfo").get("tags").asText()) + ); + } + + @Test + void shouldDoNothingWhenAddressLine2IsNullInComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": null, + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithoutCreateAndReadPermission()).build(); + Map newDataNode = getJsonMapNode(newDataString); + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, + newDataNode, getJsonMapNode(complexTypePayload), "123"); + + assertEquals(newDataNode, filteredFields); + } + + @Test + void shouldDoNothingWhenComplexTypeIsMissingInComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + } + } + } + """; + + String expectedMessage = "Missing field 'location' under 'Note'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertEquals(getJsonMapNode(newDataString), result); + } + + @Test + void shouldDoNothingWhenSubFieldIsMissingInComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'AddressLine1' under 'location'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertEquals(getJsonMapNode(newDataString), result); + } + + @Test + void shouldDoNothingWhenComplexTypeSubFieldIsMissingInComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'noteID' under 'additionalInfo'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertEquals(getJsonMapNode(newDataString), result); + } + + @Test + void shouldDoNothingWhenSubFieldIsNullInInnerComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": null + } + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithoutCreateAndReadPermission()).build(); + Map newDataNode = getJsonMapNode(newDataString); + Map filteredFields = service.filterRestrictedFields(caseTypeDefinition, + newDataNode, getJsonMapNode(complexTypePayload), "123"); + + assertEquals(newDataNode, filteredFields); + } + + @Test + void shouldDoNothingWhenComplexFieldIsNullInInnerComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": null + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'category' under 'additionalInfo'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), + () -> assertTrue(result.get("Note").get("content").get("additionalInfo").isObject()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + } + + @Test + void shouldDoNothingWhenComplexFieldIsMissingInInnerComplexNestedObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables." + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'additionalInfo' under 'content'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").get("content").has("title")), + () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), + () -> assertTrue(result.get("Note").get("content").has("body")), + () -> assertEquals("Discussion about project timelines and deliverables.", + result.get("Note").get("content").get("body").asText()), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + } + + @Test + void shouldDoNothingWhenParentComplexFieldIsMissingInMainObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'content' under 'Note'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertEquals(getJsonMapNode(newDataString), result); + } + + @Test + void shouldDoNothingWhenParentComplexFieldIsNullInObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": null, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'additionalInfo' under 'content'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), + () -> assertTrue(result.get("Note").has("metadata")), + () -> assertTrue(result.get("Note").get("metadata").has("authorName")), + () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), + () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), + () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), + () -> assertTrue(result.get("Note").has("content")), + () -> assertTrue(result.get("Note").has("location")), + () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), + () -> assertEquals("123 Main Street", + result.get("Note").get("location").get("AddressLine1").asText()), + () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), + () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), + () -> assertTrue(result.get("Note").get("location").has("City")), + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), + () -> assertTrue(result.get("Note").get("location").has("State")), + () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), + () -> assertTrue(result.get("Note").get("location").has("Country")), + () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), + () -> assertTrue(result.get("Note").get("location").has("PostalCode")), + () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) + ); + } + + @Test + void shouldDoNothingWhenSimpleFieldIsMissingInParentObjectWithoutCreateAndReadPermission() { + final String newDataString = """ + { + "Note": { + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """; + + String expectedMessage = "Missing field 'type' under 'Note'."; + + Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, + noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertEquals(getJsonMapNode(newDataString), result); + } + + @Test + void shouldDoNothingNullOfRootNoteFieldInComplexNestedObjectWithoutDeletePermission() { + final String newDataString = """ + { + "Note": null + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithoutCreateAndReadPermission()).build(); + + Map existingData = getJsonMapNode(complexTypePayload); + Map newData = getJsonMapNode(newDataString); + + Map result = service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + assertAll( + () -> assertFalse(result.isEmpty()), + () -> assertTrue(result.get("Note").isObject()), + () -> assertEquals(4, listAppender.list.size()) + ); + } + + @Test + void shouldDoNothingIfExistingDataIsEmpty() { + final String existingDataString = """ + { + } + """; + + final String newDataString = """ + { + "Note": { + "type": "test" + } + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithCreateAndWithoutReadPermission()).build(); + + Map existingData = getJsonMapNode(existingDataString); + Map newData = getJsonMapNode(newDataString); + + service.filterRestrictedFields(caseTypeDefinition, newData, existingData, "123"); + + assertEquals(0, listAppender.list.size()); + } + + @Test + void shouldDoNothingIfExistingDataRootNoteFieldIsEmpty() { + final String existingDataString = """ + { + "Note": {} + } + """; + + final String newDataString = """ + { + "Note": { + "type": "test" + } + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithCreateAndWithoutReadPermission()).build(); + + Map existingData = getJsonMapNode(existingDataString); + Map newData = getJsonMapNode(newDataString); + + service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + assertEquals(0, listAppender.list.size()); + } + + @Test + void shouldDoNothingIfParentNodeIsMissing() { + final String existingDataString = """ + { + "Note": { + "type": "test" + } + } + """; + + final String newDataString = """ + { + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithoutCreateAndReadPermission()).build(); + + Map existingData = getJsonMapNode(existingDataString); + Map newData = getJsonMapNode(newDataString); + + service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + assertEquals(0, listAppender.list.size()); + } + + @Test + void shouldAddMissingSimpleFieldToNewDataRootNoteWithCreateAndWithoutReadPermission() { + final String existingDataString = """ + { + "Note": { + "type": "test" + } + } + """; + + final String newDataString = """ + { + "Note": {} + } + """; + + setupLogging().setLevel(Level.INFO); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithCreateAndWithoutReadPermission()).build(); + + Map existingData = getJsonMapNode(existingDataString); + Map newData = getJsonMapNode(newDataString); + + Map result = service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + assertAll( + () -> assertTrue(result.get("Note").has("type")), + () -> assertEquals("test", result.get("Note").get("type").asText()), + () -> assertEquals("Adding missing field 'type' under 'Note'.", + listAppender.list.getFirst().getFormattedMessage()) + ); + + } + + @Test + void shouldDoNothingIfNewDataRootNoteFieldIsEmptyWithoutCreateAndReadPermission() { + final String existingDataString = """ + { + "Note": { + "type": "test" + } + } + """; + + final String newDataString = """ + { + "Note": {} + } + """; + + setupLogging().setLevel(Level.DEBUG); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(noteWithNestedFieldsWithoutCreateAndReadPermission()).build(); + + Map existingData = getJsonMapNode(existingDataString); + Map newData = getJsonMapNode(newDataString); + + service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + assertEquals("Missing field 'type' under 'Note'.", listAppender.list.getFirst().getFormattedMessage()); + } + + @Test + void shouldAddMissingUndefinedFieldInDocumentObjectWithCreateWithoutReadPermission() { + final String existingDataString = """ + { + "generatedCaseDocuments": [ + { + "id": "123", + "value": { + "createdBy": "Test", + "documentLink": { + "category_id": "detailsOfClaim", + "document_url": "http_document_url", + "document_filename": "document.pdf", + "document_binary_url": "http://document/binary" + }, + "documentName": "document.pdf", + "documentSize": 34805, + "documentType": "CLAIM", + "createdDatetime": "2024-10-16T10:47:03" + } + } + ] + } + """; + + final String newDataString = """ + { + "generatedCaseDocuments": [ + { + "id": "123", + "value": { + "createdBy": "Test", + "documentLink": { + "document_url": "http_document_url", + "document_filename": "document.pdf", + "document_binary_url": "http://document/binary" + }, + "documentName": "document.pdf", + "documentSize": 34805, + "documentType": "CLAIM", + "createdDatetime": "2024-10-16T10:47:03" + } + } + ] + } + """; + + String expectedMessage = "Adding missing field 'category_id' under 'documentLink'."; + + Map result = assertFilterServiceAndLogging(existingDataString, newDataString, + generatedCaseDocumentsFieldWithCreateWithoutReadPermission(), Level.INFO, expectedMessage); + + assertAll( + () -> assertTrue(result.get("generatedCaseDocuments").isArray()), + () -> assertEquals(1, result.get("generatedCaseDocuments").size()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).has("id")), + () -> assertEquals("123", result.get("generatedCaseDocuments").get(0).get("id").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).has("value")), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("createdBy")), + () -> assertEquals("Test", + result.get("generatedCaseDocuments").get(0).get("value").get("createdBy").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentLink")), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").get("documentLink").has( + "document_url")), + () -> assertEquals("http_document_url", + result.get("generatedCaseDocuments").get(0).get("value").get("documentLink") + .get("document_url").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0) + .get("value").get("documentLink").has("document_filename")), + () -> assertEquals("document.pdf", result.get("generatedCaseDocuments").get(0) + .get("value").get("documentLink").get("document_filename").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").get("documentLink") + .has("document_binary_url")), + () -> assertEquals("http://document/binary", result.get("generatedCaseDocuments").get(0) + .get("value").get("documentLink").get("document_binary_url").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").get("documentLink") + .has("category_id")), + () -> assertEquals("detailsOfClaim", result.get("generatedCaseDocuments").get(0) + .get("value").get("documentLink").get("category_id").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentName")), + () -> assertEquals("document.pdf", result.get("generatedCaseDocuments").get(0) + .get("value").get("documentName").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentSize")), + () -> assertEquals(34805, result.get("generatedCaseDocuments").get(0).get("value") + .get("documentSize").asInt()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentType")), + () -> assertEquals("CLAIM", result.get("generatedCaseDocuments").get(0).get("value") + .get("documentType").asText()), + () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("createdDatetime")), + () -> assertEquals("2024-10-16T10:47:03", result.get("generatedCaseDocuments").get(0) + .get("value").get("createdDatetime").asText()) + ); + } + + @Test + void shouldDoNothingWhenUndefinedFieldIsMissingInDocumentObjectWithoutCreateAndReadPermission() { + final String existingDataString = """ + { + "generatedCaseDocuments": [ + { + "id": "123", + "value": { + "createdBy": "Test", + "documentLink": { + "category_id": "detailsOfClaim", + "document_url": "http_document_url", + "document_filename": "document.pdf", + "document_binary_url": "http://document/binary" + }, + "documentName": "document.pdf", + "documentSize": 34805, + "documentType": "CLAIM", + "createdDatetime": "2024-10-16T10:47:03" + } + } + ] + } + """; + + final String newDataString = """ + { + "generatedCaseDocuments": [ + { + "id": "123", + "value": { + "createdBy": "Test", + "documentLink": { + "document_url": "http_document_url", + "document_filename": "document.pdf", + "document_binary_url": "http://document/binary" + }, + "documentName": "document.pdf", + "documentSize": 34805, + "documentType": "CLAIM", + "createdDatetime": "2024-10-16T10:47:03" + } + } + ] + } + """; + + String expectedMessage = "Missing field 'category_id' under 'documentLink'."; + + Map result = assertFilterServiceAndLogging(existingDataString, newDataString, + generatedCaseDocumentsFieldWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + + assertEquals(getJsonMapNode(newDataString), result); + } + + private Logger setupLogging() { + listAppender = new ListAppender<>(); + listAppender.start(); + logger = (Logger) LoggerFactory.getLogger(RestrictedFieldProcessor.class); + logger.detachAndStopAllAppenders(); + if (loggingEventList != null && !loggingEventList.isEmpty()) { + loggingEventList.clear(); + } + logger.addAppender(listAppender); + return logger; + } + + private Map getJsonMapNode(final String content) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(content, + objectMapper.getTypeFactory().constructMapType(Map.class, String.class, JsonNode.class)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + private Map assertFilterServiceAndLogging(final String existingDataString, + final String newDataString, + CaseFieldDefinition field, Level expectedLogLevel, + String expectedLogMessage) { + setupLogging().setLevel(expectedLogLevel); + + CaseTypeDefinition caseTypeDefinition = + newCaseType().withField(field).build(); + + Map existingData = getJsonMapNode(existingDataString); + Map newData = getJsonMapNode(newDataString); + + Map result = service.filterRestrictedFields(caseTypeDefinition, newData, existingData, + "123"); + + loggingEventList = listAppender.list; + + assertAll( + () -> assertTrue(loggingEventList.stream().anyMatch(log -> log.getLevel() == expectedLogLevel + && log.getFormattedMessage().equals(expectedLogMessage)), + "Expected log message not found: " + expectedLogMessage) + ); + + return result; + } + + @AfterEach + void tearDown() throws Exception { + openMocks.close(); + + if (listAppender != null) { + listAppender.stop(); + } + if (logger != null) { + logger.detachAndStopAllAppenders(); + } + } +} diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java index 685483c756..8d095d468e 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java @@ -39,6 +39,7 @@ import uk.gov.hmcts.ccd.domain.service.common.CaseService; 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.common.RestrictedFieldProcessor; import uk.gov.hmcts.ccd.domain.service.common.SecurityClassificationServiceImpl; import uk.gov.hmcts.ccd.domain.service.common.UIDService; import uk.gov.hmcts.ccd.domain.service.getcasedocument.CaseDocumentService; @@ -160,6 +161,8 @@ class CreateCaseEventServiceTest extends TestFixtures { private UIDService uidService; @Mock private ValidateCaseFieldsOperation validateCaseFieldsOperation; + @Mock + private RestrictedFieldProcessor restrictedFieldProcessor; @Spy private CaseDocumentTimestampService caseDocumentTimestampService = From 12b3de65fc69fcbb85a0bebf1439d01420f23b81 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Wed, 11 Dec 2024 13:13:45 +0000 Subject: [PATCH 2/6] Trigger build with empty commit From c224d6fac5c435e7c4398680f2727b0e9704e518 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Thu, 12 Dec 2024 10:38:47 +0000 Subject: [PATCH 3/6] sonar fixes --- .../common/RestrictedFieldProcessor.java | 163 +++-- .../common/RestrictedFieldProcessorTest.java | 687 +++++++----------- 2 files changed, 346 insertions(+), 504 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java index 4e799e7891..42900d7411 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessor.java @@ -27,6 +27,9 @@ @Service public class RestrictedFieldProcessor { + private static final String VALUE = "value"; + private static final String ID = "id"; + private final CaseAccessService caseAccessService; public RestrictedFieldProcessor(CaseAccessService caseAccessService) { @@ -48,23 +51,25 @@ public Map filterRestrictedFields(final CaseTypeDefinition cas sanitisedData.forEach((key, sanitizedValue) -> { JsonNode existingValue = existingData.get(key); - if (existingValue != null) { - CaseFieldDefinition rootFieldDefinition = caseTypeDefinition.getCaseFieldDefinitions() - .stream() - .filter(field -> field.getId().equals(key)) - .findFirst() - .orElse(null); - - if (rootFieldDefinition != null && rootFieldDefinition.isCompoundFieldType()) { - JsonNode updatedValue = processSubFieldsRecursively( - rootFieldDefinition, - sanitizedValue, - existingValue, - accessProfileNames - ); - - mergedData.put(key, updatedValue); - } + if (existingValue == null) { + return; + } + + CaseFieldDefinition rootFieldDefinition = caseTypeDefinition.getCaseFieldDefinitions() + .stream() + .filter(field -> field.getId().equals(key)) + .findFirst() + .orElse(null); + + if (rootFieldDefinition != null && rootFieldDefinition.isCompoundFieldType()) { + JsonNode updatedValue = processSubFieldsRecursively( + rootFieldDefinition, + sanitizedValue, + existingValue, + accessProfileNames + ); + + mergedData.put(key, updatedValue); } }); @@ -87,10 +92,7 @@ private JsonNode processSubFieldsRecursively(CaseFieldDefinition parentFieldDefi return sanitizedNode; } - ObjectNode sanitizedObjectNode = sanitizedNode != null && sanitizedNode.isObject() - ? (ObjectNode) sanitizedNode.deepCopy() - : JsonNodeFactory.instance.objectNode(); - + ObjectNode sanitizedObjectNode = initializeSanitizedObjectNode(sanitizedNode); ObjectNode existingObjectNode = (ObjectNode) existingNode; existingObjectNode.fieldNames().forEachRemaining(fieldName -> { @@ -118,56 +120,97 @@ private JsonNode processSubFieldsRecursively(CaseFieldDefinition parentFieldDefi return sanitizedObjectNode; } + private static ObjectNode initializeSanitizedObjectNode(JsonNode sanitizedNode) { + return sanitizedNode != null && sanitizedNode.isObject() + ? (ObjectNode) sanitizedNode.deepCopy() + : JsonNodeFactory.instance.objectNode(); + } + private JsonNode processCollectionFields(CaseFieldDefinition subFieldDefinition, JsonNode sanitizedArrayNode, JsonNode existingArrayNode, Set accessProfileNames) { + ArrayNode sanitizedArray = initializeSanitizedArrayNode(sanitizedArrayNode); + ArrayNode existingArray = (ArrayNode) existingArrayNode; - ArrayNode sanitizedArray = sanitizedArrayNode != null && sanitizedArrayNode.isArray() + for (JsonNode existingItem : existingArray) { + processExistingItem(subFieldDefinition, sanitizedArray, existingItem, accessProfileNames); + } + + return sanitizedArray; + } + + private ArrayNode initializeSanitizedArrayNode(JsonNode sanitizedArrayNode) { + return sanitizedArrayNode != null && sanitizedArrayNode.isArray() ? (ArrayNode) sanitizedArrayNode.deepCopy() : JsonNodeFactory.instance.arrayNode(); + } - ArrayNode existingArray = (ArrayNode) existingArrayNode; + private void processExistingItem(CaseFieldDefinition subFieldDefinition, + ArrayNode sanitizedArray, + JsonNode existingItem, + Set accessProfileNames) { + JsonNode existingItemId = existingItem.get(ID); - for (JsonNode existingItem : existingArray) { - JsonNode existingItemId = existingItem.get("id"); + Optional matchingNewItem = findMatchingNewItem(sanitizedArray, existingItemId); - Optional matchingNewItem = StreamSupport.stream(sanitizedArray.spliterator(), false) - .filter(newItem -> !isNullId(newItem) && newItem.get("id").equals(existingItemId)) - .findFirst(); + if (matchingNewItem.isEmpty()) { + handleMissingItem(subFieldDefinition, sanitizedArray, existingItem, accessProfileNames, existingItemId); + } else { + processMatchingItem(subFieldDefinition, matchingNewItem.get(), existingItem, accessProfileNames); + } + } - if (matchingNewItem.isEmpty()) { - log.debug("Missing collection item with ID '{}' under '{}'.", existingItemId, - subFieldDefinition.getId()); + private Optional findMatchingNewItem(ArrayNode sanitizedArray, JsonNode existingItemId) { + return StreamSupport.stream(sanitizedArray.spliterator(), false) + .filter(newItem -> !isNullId(newItem) && newItem.get(ID).equals(existingItemId)) + .findFirst(); + } - if (isCreateWithoutReadAllowed(subFieldDefinition.getAccessControlLists(), accessProfileNames)) { - log.info("Adding missing collection item with ID '{}' under '{}'.", existingItemId, - subFieldDefinition.getId()); - sanitizedArray.add(existingItem); - } - } else { - JsonNode newValueField = matchingNewItem.get().get("value"); - JsonNode existingValueField = existingItem.get("value"); - - if (existingValueField != null) { - JsonNode processedValueField; - - if (existingValueField.isObject()) { - processedValueField = processSubFieldsRecursively(subFieldDefinition, - newValueField, - existingValueField, - accessProfileNames); - } else { - processedValueField = processSimpleValueField( - subFieldDefinition, newValueField, existingValueField, accessProfileNames); - } - - ((ObjectNode) matchingNewItem.get()).set("value", processedValueField); - } - } + private void handleMissingItem(CaseFieldDefinition subFieldDefinition, + ArrayNode sanitizedArray, + JsonNode existingItem, + Set accessProfileNames, + JsonNode existingItemId) { + log.debug("Missing collection item with ID '{}' under '{}'.", existingItemId, + subFieldDefinition.getId()); + + if (isCreateWithoutReadAllowed(subFieldDefinition.getAccessControlLists(), accessProfileNames)) { + log.info("Adding missing collection item with ID '{}' under '{}'.", existingItemId, + subFieldDefinition.getId()); + sanitizedArray.add(existingItem); } + } - return sanitizedArray; + private void processMatchingItem(CaseFieldDefinition subFieldDefinition, + JsonNode matchingNewItem, + JsonNode existingItem, + Set accessProfileNames) { + JsonNode newValueField = matchingNewItem.get(VALUE); + JsonNode existingValueField = existingItem.get(VALUE); + + if (existingValueField != null) { + JsonNode processedValueField = processValueField(subFieldDefinition, newValueField, existingValueField, + accessProfileNames); + ((ObjectNode) matchingNewItem).set(VALUE, processedValueField); + } + } + + private JsonNode processValueField(CaseFieldDefinition subFieldDefinition, + JsonNode newValueField, + JsonNode existingValueField, + Set accessProfileNames) { + if (existingValueField.isObject()) { + return processSubFieldsRecursively(subFieldDefinition, + newValueField, + existingValueField, + accessProfileNames); + } else { + return processSimpleValueField(subFieldDefinition, + newValueField, + existingValueField, + accessProfileNames); + } } private JsonNode processSimpleValueField(CaseFieldDefinition subFieldDefinition, JsonNode newValueField, @@ -186,9 +229,9 @@ private JsonNode processSimpleValueField(CaseFieldDefinition subFieldDefinition, } private boolean isNullId(JsonNode newItem) { - return newItem.get("id") == null - || newItem.get("id").equals(NullNode.getInstance()) - || "null".equalsIgnoreCase(newItem.get("id").asText()); + return newItem.get(ID) == null + || newItem.get(ID).equals(NullNode.getInstance()) + || "null".equalsIgnoreCase(newItem.get(ID).asText()); } private boolean isCreateWithoutReadAllowed(List fieldAccessControlLists, diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java index 4614fb3332..13cc35c60d 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/common/RestrictedFieldProcessorTest.java @@ -10,6 +10,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.slf4j.LoggerFactory; @@ -19,6 +21,7 @@ import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -110,7 +113,7 @@ class RestrictedFieldProcessorTest { } """; - static String nestedComplexTypeArrayPayload = """ + static String nestedCaseCategoryComplexTypeArrayPayload = """ { "caseCategory": { "value": { @@ -691,7 +694,8 @@ void shouldAddMissingValueComplexFieldToComplexTypeWhenCreateWithoutReadPermissi String expectedMessage = "Adding missing field 'value' under 'caseCategory'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithCreateWithoutReadPermission(), Level.INFO, expectedMessage); @@ -726,7 +730,8 @@ void shouldAddMissingValueSubFieldsToComplexTypeWhenCreateWithoutReadPermission( String expectedMessage = "Adding missing field 'code' under 'value'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithCreateWithoutReadPermission(), Level.INFO, expectedMessage); @@ -755,7 +760,8 @@ void shouldAddMissingListItemsArrayToComplexTypeWhenCreateWithoutReadPermission( String expectedMessage = "Adding missing field 'list_items' under 'caseCategory'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithCreateWithoutReadPermission(), Level.INFO, expectedMessage); @@ -791,7 +797,8 @@ void shouldAddMissingListItemsNodeToComplexTypeWhenCreateWithoutReadPermission() String expectedMessage = "Adding missing collection item with ID '\"123456\"' under 'list_items'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithCreateWithoutReadPermission(), Level.INFO, expectedMessage); @@ -823,7 +830,8 @@ void shouldDoNothingForMissingValueComplexFieldToComplexTypeWhenWithoutCreateAnd String expectedMessage = "Missing field 'value' under 'caseCategory'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); @@ -855,7 +863,8 @@ void shouldDoNothingForNullValueWhenWithoutCreateAndReadPermission() { String expectedMessage = "Missing field 'label' under 'value'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); @@ -883,7 +892,8 @@ void shouldDoNothingForMissingListItemArrayWhenWithoutCreateAndReadPermission() String expectedMessage = "Missing field 'list_items' under 'caseCategory'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); @@ -920,7 +930,8 @@ void shouldDoNothingForMissingListItemNodeWhenWithoutCreateAndReadPermission() { String expectedMessage = "Missing collection item with ID '\"123456\"' under 'list_items'."; - Map result = assertFilterServiceAndLogging(nestedComplexTypeArrayPayload, newDataString, + Map result = assertFilterServiceAndLogging(nestedCaseCategoryComplexTypeArrayPayload, + newDataString, caseCategoryFieldWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); @@ -2007,20 +2018,17 @@ void shouldAddChangedCollectionTypeNodeIDWithoutCreateWithoutReadPermission() { assertAll( () -> assertTrue(result.containsKey("Tags")), () -> assertEquals(3, result.get("Tags").size()), - () -> assertTrue(result.get("Tags").get(0).has("value")), () -> assertTrue(result.get("Tags").get(0).get("value").has("Tag")), () -> assertTrue(result.get("Tags").get(0).get("value").has("Category")), () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), () -> assertTrue(result.get("Tags").get(0).has("id")), () -> assertEquals("999", result.get("Tags").get(0).get("id").asText()), - () -> assertTrue(result.get("Tags").get(1).has("value")), () -> assertTrue(result.get("Tags").get(1).get("value").has("Tag")), () -> assertTrue(result.get("Tags").get(1).get("value").has("Category")), () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()), - () -> assertTrue(result.get("Tags").get(2).has("value")), () -> assertTrue(result.get("Tags").get(2).get("value").has("Tag")), () -> assertTrue(result.get("Tags").get(2).get("value").has("Category")), () -> assertEquals("private", result.get("Tags").get(2).get("value").get("Tag").asText()), @@ -2029,70 +2037,82 @@ void shouldAddChangedCollectionTypeNodeIDWithoutCreateWithoutReadPermission() { ); } - @Test - void shouldAddMissingCollectionTypeSubFieldsWithoutCreateWithoutReadPermission() { - final String newDataString = """ - { - "Tags": [ - { - "id": "123" - }, - { - "value": { - "Tag": "public", - "Category": "Work" - }, - "id": "456" - } - ] - } - """; - - String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; - - Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, - tagsWithCreatePermissionWithoutReadPermission(), - Level.INFO, expectedMessage); - - assertAll( - () -> assertTrue(result.containsKey("Tags")), - () -> assertEquals(2, result.get("Tags").size()), - () -> assertTrue(result.get("Tags").get(0).has("id")), - () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), - () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), - () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), - () -> assertTrue(result.get("Tags").get(1).has("value")), - () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), - () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), - () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) + private static Stream tagsTestCases() { + return Stream.of( + new TestCase( + """ + { + "Tags": [ + { + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """, + "Adding missing field 'Tag' under 'Tags'." + ), + new TestCase( + """ + { + "Tags": [ + { + "value": null, + "id": "123" + }, + { + "value": { + "Tag": "public", + "Category": "Work" + }, + "id": "456" + } + ] + } + """, + "Adding missing field 'Tag' under 'Tags'." + ), + new TestCase( + """ + { + "Tags": [ + { + "value": { + "Tag": "private", + "Category": "Personal" + }, + "id": "123" + }, + { + "value": { + "Category": "Work" + }, + "id": "456" + } + ] + } + """, + "Adding missing field 'Tag' under 'Tags'." + ) ); } - @Test - void shouldAddNullCollectionTypeValueWithoutCreateWithoutReadPermission() { - final String newDataString = """ - { - "Tags": [ - { - "value": null, - "id": "123" - }, - { - "value": { - "Tag": "public", - "Category": "Work" - }, - "id": "456" - } - ] - } - """; - - String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; - - Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, + @ParameterizedTest + @MethodSource("tagsTestCases") + void shouldHandleTagFilteringWithCreatePermissionWithoutReadPermission(TestCase testCase) { + Map result = assertFilterServiceAndLogging( + arrayPayload, + testCase.inputJson, tagsWithCreatePermissionWithoutReadPermission(), - Level.INFO, expectedMessage); + Level.INFO, + testCase.expectedMessage + ); assertAll( () -> assertTrue(result.containsKey("Tags")), @@ -2108,46 +2128,7 @@ void shouldAddNullCollectionTypeValueWithoutCreateWithoutReadPermission() { ); } - @Test - void shouldAddMissingCollectionTypeSecondNodeFieldWithoutCreateWithoutReadPermission() { - final String newDataString = """ - { - "Tags": [ - { - "value": { - "Tag": "private", - "Category": "Personal" - }, - "id": "123" - }, - { - "value": { - "Category": "Work" - }, - "id": "456" - } - ] - } - """; - - String expectedMessage = "Adding missing field 'Tag' under 'Tags'."; - - Map result = assertFilterServiceAndLogging(arrayPayload, newDataString, - tagsWithCreatePermissionWithoutReadPermission(), - Level.INFO, expectedMessage); - - assertAll( - () -> assertTrue(result.containsKey("Tags")), - () -> assertEquals(2, result.get("Tags").size()), - () -> assertTrue(result.get("Tags").get(0).has("id")), - () -> assertEquals("123", result.get("Tags").get(0).get("id").asText()), - () -> assertEquals("private", result.get("Tags").get(0).get("value").get("Tag").asText()), - () -> assertEquals("Personal", result.get("Tags").get(0).get("value").get("Category").asText()), - () -> assertTrue(result.get("Tags").get(1).has("value")), - () -> assertEquals("public", result.get("Tags").get(1).get("value").get("Tag").asText()), - () -> assertEquals("Work", result.get("Tags").get(1).get("value").get("Category").asText()), - () -> assertEquals("456", result.get("Tags").get(1).get("id").asText()) - ); + private record TestCase(String inputJson, String expectedMessage) { } @Test @@ -2777,38 +2758,22 @@ void shouldAddMissingLocationFieldInComplexNestedObjectWithCreateAndWithoutReadP noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), - () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content") .get("additionalInfo").get("tags").asText()), - () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("123 Main Street", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), - () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")) + () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()) ); } @@ -2848,42 +2813,24 @@ void shouldNotPermitNullOfAddressLine2FieldInComplexNestedObjectWithCreateAndWit noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), - () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content").get("additionalInfo").get("tags").asText()), - () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("123 Main Street", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) ); } @@ -2924,41 +2871,25 @@ void shouldAddMissingSubFieldInInnerComplexNestedObjectWithCreateAndWithoutReadP noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content").get("additionalInfo").get("tags").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) ); @@ -3037,41 +2968,24 @@ void shouldAddMissingSubFieldsOfNullComplexFieldInInnerComplexNestedObjectWithCr noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content").get("additionalInfo").get("tags").asText()), - () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) ); } @@ -3108,41 +3022,23 @@ void shouldAddMissingComplexFieldInInnerComplexNestedObjectWithCreateAndWithoutR noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), - () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content").get("additionalInfo").get("tags").asText()), - () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) ); } @@ -3175,37 +3071,22 @@ void shouldAddMissingParentComplexFieldWithCreateAndWithoutReadPermission() { noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), - () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("location")), () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("123 Main Street", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), @@ -3243,41 +3124,25 @@ void shouldAddNullParentComplexFieldWithCreateAndWithoutReadPermission() { noteWithNestedFieldsWithCreateAndWithoutReadPermission(), Level.INFO, expectedMessage); assertAll( - () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("123 Main Street", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content").get("additionalInfo").get("tags").asText()) ); @@ -3321,39 +3186,24 @@ void shouldAddMissingTopSimpleFieldInParentNodeWithCreateAndWithoutReadPermissio assertAll( () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("location")), () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("123 Main Street", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("noteID")), () -> assertEquals("abc123", result.get("Note").get("content").get("additionalInfo").get("noteID").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("category")), () -> assertEquals("Meeting", result.get("Note").get("content").get("additionalInfo").get("category").asText()), - () -> assertTrue(result.get("Note").get("content").get("additionalInfo").has("tags")), () -> assertEquals("project, timeline, deliverable", result.get("Note").get("content").get("additionalInfo").get("tags").asText()) ); @@ -3399,111 +3249,158 @@ void shouldDoNothingWhenAddressLine2IsNullInComplexNestedObjectWithoutCreateAndR assertEquals(newDataNode, filteredFields); } - @Test - void shouldDoNothingWhenComplexTypeIsMissingInComplexNestedObjectWithoutCreateAndReadPermission() { - final String newDataString = """ - { - "Note": { - "type": "PersonalNote", - "metadata": { - "authorName": "John Doe", - "creationDate": "2024-11-04" - }, - "content": { - "title": "Meeting Notes", - "body": "Discussion about project timelines and deliverables.", - "additionalInfo": { - "noteID": "abc123", - "category": "Meeting", - "tags": "project, timeline, deliverable" - } - } - } - } - """; - - String expectedMessage = "Missing field 'location' under 'Note'."; - - Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, - noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); - - assertEquals(getJsonMapNode(newDataString), result); - } - - @Test - void shouldDoNothingWhenSubFieldIsMissingInComplexNestedObjectWithoutCreateAndReadPermission() { - final String newDataString = """ - { - "Note": { - "type": "PersonalNote", - "metadata": { - "authorName": "John Doe", - "creationDate": "2024-11-04" - }, - "content": { - "title": "Meeting Notes", - "body": "Discussion about project timelines and deliverables.", - "additionalInfo": { - "noteID": "abc123", - "category": "Meeting", - "tags": "project, timeline, deliverable" - } - }, - "location": { - "AddressLine2": "Suite 500", - "City": "Anytown", - "State": "Anystate", - "Country": "AnyCountry", - "PostalCode": "12345" - } - } - } - """; - - String expectedMessage = "Missing field 'AddressLine1' under 'location'."; - - Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, - noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); - - assertEquals(getJsonMapNode(newDataString), result); + private static Stream noteTestCases() { + return Stream.of( + new TestCase( + """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + } + } + } + """, + "Missing field 'location' under 'Note'." + ), + new TestCase( + """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """, + "Missing field 'AddressLine1' under 'location'." + ), + new TestCase( + """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "address1", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """, + "Missing field 'noteID' under 'additionalInfo'." + ), + new TestCase( + """ + { + "Note": { + "type": "PersonalNote", + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """, + "Missing field 'content' under 'Note'." + ), + new TestCase( + """ + { + "Note": { + "metadata": { + "authorName": "John Doe", + "creationDate": "2024-11-04" + }, + "content": { + "title": "Meeting Notes", + "body": "Discussion about project timelines and deliverables.", + "additionalInfo": { + "noteID": "abc123", + "category": "Meeting", + "tags": "project, timeline, deliverable" + } + }, + "location": { + "AddressLine1": "123 Main Street", + "AddressLine2": "Suite 500", + "City": "Anytown", + "State": "Anystate", + "Country": "AnyCountry", + "PostalCode": "12345" + } + } + } + """, + "Missing field 'type' under 'Note'." + ) + ); } - @Test - void shouldDoNothingWhenComplexTypeSubFieldIsMissingInComplexNestedObjectWithoutCreateAndReadPermission() { - final String newDataString = """ - { - "Note": { - "type": "PersonalNote", - "metadata": { - "authorName": "John Doe", - "creationDate": "2024-11-04" - }, - "content": { - "title": "Meeting Notes", - "body": "Discussion about project timelines and deliverables.", - "additionalInfo": { - "category": "Meeting", - "tags": "project, timeline, deliverable" - } - }, - "location": { - "AddressLine1": "address1", - "AddressLine2": "Suite 500", - "City": "Anytown", - "State": "Anystate", - "Country": "AnyCountry", - "PostalCode": "12345" - } - } - } - """; - - String expectedMessage = "Missing field 'noteID' under 'additionalInfo'."; - - Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, - noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); + @ParameterizedTest + @MethodSource("noteTestCases") + void shouldHandleMissingFieldsInComplexNestedObjects(TestCase testCase) { + Map result = assertFilterServiceAndLogging( + complexTypePayload, + testCase.inputJson, + noteWithNestedFieldsWithoutCreateAndReadPermission(), + Level.DEBUG, + testCase.expectedMessage + ); - assertEquals(getJsonMapNode(newDataString), result); + assertEquals(getJsonMapNode(testCase.inputJson), result); } @Test @@ -3581,31 +3478,20 @@ void shouldDoNothingWhenComplexFieldIsNullInInnerComplexNestedObjectWithoutCreat assertAll( () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), - () -> assertTrue(result.get("Note").get("content").has("additionalInfo")), () -> assertTrue(result.get("Note").get("content").get("additionalInfo").isObject()), () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) ); } @@ -3644,63 +3530,22 @@ void shouldDoNothingWhenComplexFieldIsMissingInInnerComplexNestedObjectWithoutCr assertAll( () -> assertTrue(result.get("Note").has("type")), () -> assertEquals("PersonalNote", result.get("Note").get("type").asText()), - () -> assertTrue(result.get("Note").has("metadata")), () -> assertTrue(result.get("Note").get("metadata").has("authorName")), () -> assertEquals("John Doe", result.get("Note").get("metadata").get("authorName").asText()), - () -> assertTrue(result.get("Note").get("metadata").has("creationDate")), () -> assertEquals("2024-11-04", result.get("Note").get("metadata").get("creationDate").asText()), - () -> assertTrue(result.get("Note").has("content")), - () -> assertTrue(result.get("Note").get("content").has("title")), () -> assertEquals("Meeting Notes", result.get("Note").get("content").get("title").asText()), - () -> assertTrue(result.get("Note").get("content").has("body")), () -> assertEquals("Discussion about project timelines and deliverables.", result.get("Note").get("content").get("body").asText()), () -> assertTrue(result.get("Note").has("location")), - () -> assertTrue(result.get("Note").get("location").has("AddressLine1")), () -> assertEquals("address1", result.get("Note").get("location").get("AddressLine1").asText()), - () -> assertTrue(result.get("Note").get("location").has("AddressLine2")), () -> assertEquals("Suite 500", result.get("Note").get("location").get("AddressLine2").asText()), - () -> assertTrue(result.get("Note").get("location").has("City")), () -> assertEquals("Anytown", result.get("Note").get("location").get("City").asText()), - () -> assertTrue(result.get("Note").get("location").has("State")), () -> assertEquals("Anystate", result.get("Note").get("location").get("State").asText()), - () -> assertTrue(result.get("Note").get("location").has("Country")), () -> assertEquals("AnyCountry", result.get("Note").get("location").get("Country").asText()), - () -> assertTrue(result.get("Note").get("location").has("PostalCode")), () -> assertEquals("12345", result.get("Note").get("location").get("PostalCode").asText()) ); } - @Test - void shouldDoNothingWhenParentComplexFieldIsMissingInMainObjectWithoutCreateAndReadPermission() { - final String newDataString = """ - { - "Note": { - "type": "PersonalNote", - "metadata": { - "authorName": "John Doe", - "creationDate": "2024-11-04" - }, - "location": { - "AddressLine1": "123 Main Street", - "AddressLine2": "Suite 500", - "City": "Anytown", - "State": "Anystate", - "Country": "AnyCountry", - "PostalCode": "12345" - } - } - } - """; - - String expectedMessage = "Missing field 'content' under 'Note'."; - - Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, - noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); - - assertEquals(getJsonMapNode(newDataString), result); - } - @Test void shouldDoNothingWhenParentComplexFieldIsNullInObjectWithoutCreateAndReadPermission() { final String newDataString = """ @@ -3755,44 +3600,6 @@ void shouldDoNothingWhenParentComplexFieldIsNullInObjectWithoutCreateAndReadPerm ); } - @Test - void shouldDoNothingWhenSimpleFieldIsMissingInParentObjectWithoutCreateAndReadPermission() { - final String newDataString = """ - { - "Note": { - "metadata": { - "authorName": "John Doe", - "creationDate": "2024-11-04" - }, - "content": { - "title": "Meeting Notes", - "body": "Discussion about project timelines and deliverables.", - "additionalInfo": { - "noteID": "abc123", - "category": "Meeting", - "tags": "project, timeline, deliverable" - } - }, - "location": { - "AddressLine1": "123 Main Street", - "AddressLine2": "Suite 500", - "City": "Anytown", - "State": "Anystate", - "Country": "AnyCountry", - "PostalCode": "12345" - } - } - } - """; - - String expectedMessage = "Missing field 'type' under 'Note'."; - - Map result = assertFilterServiceAndLogging(complexTypePayload, newDataString, - noteWithNestedFieldsWithoutCreateAndReadPermission(), Level.DEBUG, expectedMessage); - - assertEquals(getJsonMapNode(newDataString), result); - } - @Test void shouldDoNothingNullOfRootNoteFieldInComplexNestedObjectWithoutDeletePermission() { final String newDataString = """ @@ -4027,13 +3834,9 @@ void shouldAddMissingUndefinedFieldInDocumentObjectWithCreateWithoutReadPermissi assertAll( () -> assertTrue(result.get("generatedCaseDocuments").isArray()), () -> assertEquals(1, result.get("generatedCaseDocuments").size()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).has("id")), () -> assertEquals("123", result.get("generatedCaseDocuments").get(0).get("id").asText()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).has("value")), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("createdBy")), () -> assertEquals("Test", result.get("generatedCaseDocuments").get(0).get("value").get("createdBy").asText()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentLink")), () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").get("documentLink").has( "document_url")), () -> assertEquals("http_document_url", @@ -4051,16 +3854,12 @@ void shouldAddMissingUndefinedFieldInDocumentObjectWithCreateWithoutReadPermissi .has("category_id")), () -> assertEquals("detailsOfClaim", result.get("generatedCaseDocuments").get(0) .get("value").get("documentLink").get("category_id").asText()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentName")), () -> assertEquals("document.pdf", result.get("generatedCaseDocuments").get(0) .get("value").get("documentName").asText()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentSize")), () -> assertEquals(34805, result.get("generatedCaseDocuments").get(0).get("value") .get("documentSize").asInt()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("documentType")), () -> assertEquals("CLAIM", result.get("generatedCaseDocuments").get(0).get("value") .get("documentType").asText()), - () -> assertTrue(result.get("generatedCaseDocuments").get(0).get("value").has("createdDatetime")), () -> assertEquals("2024-10-16T10:47:03", result.get("generatedCaseDocuments").get(0) .get("value").get("createdDatetime").asText()) ); From 9e927978cca0b1291aee86255314fcee124d33d8 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Wed, 18 Dec 2024 13:28:40 +0000 Subject: [PATCH 4/6] add integration tests --- .../uk/gov/hmcts/ccd/WireMockBaseTest.java | 2 +- .../endpoint/std/CaseDetailsEndpointIT.java | 414 ++++++++++++ ...ase-definition-cu-permission-filtered.json | 612 ++++++++++++++++++ src/test/resources/sql/insert_cases.sql | 94 +++ 4 files changed, 1121 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/mappings/bookcase-definition-cu-permission-filtered.json diff --git a/src/test/java/uk/gov/hmcts/ccd/WireMockBaseTest.java b/src/test/java/uk/gov/hmcts/ccd/WireMockBaseTest.java index 5d88508928..2bb1170136 100644 --- a/src/test/java/uk/gov/hmcts/ccd/WireMockBaseTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/WireMockBaseTest.java @@ -67,7 +67,7 @@ public abstract class WireMockBaseTest extends AbstractBaseIntegrationTest { public static final String CASE_01_TYPE = "TestAddressBookCase"; public static final String CASE_02_TYPE = "TestAddressBookCase"; public static final String CASE_03_TYPE = "TestAddressBookCase"; - public static final int NUMBER_OF_CASES = 23; + public static final int NUMBER_OF_CASES = 25; public static final JSONObject responseJson1 = new JSONObject(""" { "user_task": { 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..3942836008 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 @@ -1,5 +1,9 @@ package uk.gov.hmcts.ccd.endpoint.std; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -13,6 +17,7 @@ import org.mockito.MockitoAnnotations; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; +import org.slf4j.LoggerFactory; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.MediaType; import org.springframework.jdbc.core.BeanPropertyRowMapper; @@ -46,6 +51,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.model.std.MessageQueueCandidate; +import uk.gov.hmcts.ccd.domain.service.common.RestrictedFieldProcessor; import javax.inject.Inject; import java.nio.charset.StandardCharsets; @@ -65,6 +71,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasProperty; @@ -104,6 +111,7 @@ public class CaseDetailsEndpointIT extends WireMockBaseTest { private static final String UPLOAD_TIMESTAMP = "2000-02-29T00:00:00.000000000"; private static final JsonNodeFactory JSON_NODE_FACTORY = new JsonNodeFactory(false); private static final String CASE_TYPE = "TestAddressBookCase"; + private static final String CASE_TYPE_FILTERED = "TestAddressBookCaseFiltered"; private static final String CASE_TYPE_VALIDATE = "TestAddressBookCaseValidate"; private static final String CASE_TYPE_VALIDATE_MULTI_PAGE = "TestAddressBookCaseValidateMultiPage"; private static final String CASE_TYPE_NO_CREATE_CASE_ACCESS = "TestAddressBookCaseNoCreateCaseAccess"; @@ -5596,6 +5604,412 @@ public void shouldReturn201CaseCreatedButNotInsertCaseLinkInDBWhenCaseLinkIsBLan assertCaseLinks(expectedCaseId, Collections.emptyList()); } + private Logger logger; + private ListAppender listAppender; + private List loggingEventList; + + private Logger setupLogging() { + listAppender = new ListAppender<>(); + listAppender.start(); + logger = (Logger) LoggerFactory.getLogger(RestrictedFieldProcessor.class); + logger.detachAndStopAllAppenders(); + if (loggingEventList != null && !loggingEventList.isEmpty()) { + loggingEventList.clear(); + } + logger.addAppender(listAppender); + return logger; + } + + @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) + public void shouldNotPerformActionWhenFieldsMissingAndReadFalseCreateTrueMismatch() throws Exception { + final String caseReference = "1504259907353529"; + final String URL = + "/caseworkers/" + UID + "/jurisdictions/" + JURISDICTION + "/case-types/" + CASE_TYPE + + "/cases/" + caseReference + "/events"; + final CaseDataContent caseDetailsToSave = newCaseDataContent().build(); + caseDetailsToSave.setEvent(createEvent(PRE_STATES_EVENT_ID, SUMMARY, DESCRIPTION)); + + final JsonNode sanitizedData = mapper.readTree( + "{" + + "\"PersonAddress\":{" + + "\"Country\":\"_ Wales\"," + + "\"Postcode\":\"W11 5DF\"," + + "\"AddressLine1\":\"_ Flat 9\"," + + "\"AddressLine3\":\"_ ButtonVillie\"}," + + "\"PersonLastName\":\"_ Roof\"," + + "\"PersonFirstName\":\"_ George\"," + + "\"D8Document\":{" + + " \"document_url\": \"http://localhost:" + getPort() + + "/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0\"," + + " \"document_binary_url\": \"http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0" + + "/binary\"," + + " \"document_filename\": \"Seagulls_Square.jpg\"" + + "}" + + "}"); + + final JsonNode data = mapper.readTree(createExampleEventDataWithMissingItems()); + caseDetailsToSave.setData(JacksonUtils.convertValue(data)); + final String expectedClassificationString = "{" + + " \"PersonAddress\":{" + + " \"classification\": \"PUBLIC\"," + + " \"value\": {" + + " \"Country\":\"PUBLIC\"," + + " \"Postcode\":\"PUBLIC\"," + + " \"AddressLine1\":\"PUBLIC\"," + + " \"AddressLine3\":\"PUBLIC\"" + + " }" + + " }," + + " \"PersonLastName\":\"PUBLIC\"," + + " \"PersonFirstName\":\"PUBLIC\"," + + " \"D8Document\": \"PUBLIC\"" + + "}"; + final String token = generateEventToken(template, + UID, JURISDICTION, CASE_TYPE, caseReference, PRE_STATES_EVENT_ID); + caseDetailsToSave.setToken(token); + + final Level logLevel = Level.DEBUG; + setupLogging().setLevel(logLevel); + + final MvcResult mvcResult = mockMvc.perform(post(URL) + .contentType(JSON_CONTENT_TYPE) + .content(mapper.writeValueAsBytes(caseDetailsToSave)) + ).andExpect(status().is(201)) + .andReturn(); + + final String expectedLogMessage = "Missing field 'AddressLine2' under 'PersonAddress'."; + loggingEventList = listAppender.list; + + assertTrue("Expected log message not found: " + expectedLogMessage, + loggingEventList.stream().anyMatch(log -> log.getLevel() == logLevel + && log.getFormattedMessage().equals(expectedLogMessage))); + assertEqualsSanitizedData(sanitizedData, mvcResult); + + final List caseDetailsList = template.query("SELECT * FROM case_data", this::mapCaseData); + assertEquals("Incorrect number of cases: No case should be created", NUMBER_OF_CASES, caseDetailsList.size()); + + final CaseDetails savedCaseDetails = caseDetailsList.stream() + .filter(c -> caseReference.equals(c.getReference().toString())) + .findFirst() + .orElse(null); + assertNotNull(savedCaseDetails); + assertEquals("Incorrect Case Type", CASE_TYPE, savedCaseDetails.getCaseTypeId()); + Map sanitizedDataMap = JacksonUtils.convertValue(sanitizedData); + assertThat( + "Incorrect Data content: Data should have changed", + savedCaseDetails.getData().entrySet(), + containsInAnyOrder(sanitizedDataMap.entrySet().toArray()) + ); + assertEquals("State should have been updated", "state4", savedCaseDetails.getState()); + JSONAssert.assertEquals(expectedClassificationString, + mapper.convertValue(savedCaseDetails.getDataClassification(), JsonNode.class).toString(), JSONCompareMode + .LENIENT); + + final List caseAuditEventList = template.query("SELECT * FROM case_event", this::mapAuditEvent); + assertEquals("A new event should have been created", 6, caseAuditEventList.size()); + + // Assertion belows are for creation event + final AuditEvent caseAuditEvent = caseAuditEventList.getLast(); + assertAll( + () -> assertEquals(sanitizedDataMap, caseAuditEvent.getData()), + () -> assertEquals("123", caseAuditEvent.getUserId()), + () -> assertEquals("Strife", caseAuditEvent.getUserLastName()), + () -> assertEquals("Cloud", caseAuditEvent.getUserFirstName()), + () -> assertEquals("HAS PRE STATES EVENT", caseAuditEvent.getEventName()), + () -> assertEquals(savedCaseDetails.getId(), caseAuditEvent.getCaseDataId()), + () -> assertEquals(savedCaseDetails.getCaseTypeId(), caseAuditEvent.getCaseTypeId()), + () -> assertEquals(1, caseAuditEvent.getCaseTypeVersion().intValue()), + () -> assertEquals(savedCaseDetails.getState(), caseAuditEvent.getStateId()), + () -> assertEquals("Case in state 4", caseAuditEvent.getStateName()), + () -> assertEquals(savedCaseDetails.getData(), caseAuditEvent.getData()), + () -> assertEquals(SUMMARY, caseAuditEvent.getSummary()), + () -> assertEquals(DESCRIPTION, caseAuditEvent.getDescription()), + () -> JSONAssert.assertEquals(expectedClassificationString, + mapper.convertValue(caseAuditEvent.getDataClassification(), JsonNode.class).toString(), + JSONCompareMode.LENIENT) + ); + } + + @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) + public void shouldAddMissingFieldsBackToDataWhenReadFalseCreateTrueMatch() throws Exception { + final String caseReference = "1202264432028419"; + final String URL = + "/caseworkers/" + UID + "/jurisdictions/" + JURISDICTION + "/case-types/" + CASE_TYPE_FILTERED + + "/cases/" + caseReference + "/events"; + final CaseDataContent caseDetailsToSave = newCaseDataContent().build(); + caseDetailsToSave.setEvent(createEvent(PRE_STATES_EVENT_ID, SUMMARY, DESCRIPTION)); + + final JsonNode sanitizedData = mapper.readTree( + "{" + + "\"PersonAddress\":{" + + "\"Country\":\"_ Wales\"," + + "\"Postcode\":\"W11 5DF\"," + + "\"AddressLine1\":\"_ Flat 9\"," + + "\"AddressLine2\":\"Fake Street\"," + + "\"AddressLine3\":\"_ ButtonVillie\"}," + + "\"PersonLastName\":\"_ Roof\"," + + "\"PersonFirstName\":\"_ George\"," + + "\"D8Document\":{" + + " \"document_url\": \"http://localhost:" + getPort() + + "/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0\"," + + " \"document_binary_url\": \"http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0" + + "/binary\"," + + " \"document_filename\": \"Seagulls_Square.jpg\"" + + "}" + + "}"); + + final JsonNode data = mapper.readTree(createExampleEventDataWithMissingItems()); + caseDetailsToSave.setData(JacksonUtils.convertValue(data)); + final String expectedClassificationString = "{" + + " \"PersonAddress\":{" + + " \"classification\": \"PUBLIC\"," + + " \"value\": {" + + " \"Country\":\"PUBLIC\"," + + " \"Postcode\":\"PUBLIC\"," + + " \"AddressLine1\":\"PUBLIC\"," + + " \"AddressLine3\":\"PUBLIC\"" + + " }" + + " }," + + " \"PersonLastName\":\"PUBLIC\"," + + " \"PersonFirstName\":\"PUBLIC\"," + + " \"D8Document\": \"PUBLIC\"" + + "}"; + final String token = generateEventToken(template, + UID, JURISDICTION, CASE_TYPE_FILTERED, caseReference, PRE_STATES_EVENT_ID); + caseDetailsToSave.setToken(token); + + final Level logLevel = Level.INFO; + setupLogging().setLevel(logLevel); + + final MvcResult mvcResult = mockMvc.perform(post(URL) + .contentType(JSON_CONTENT_TYPE) + .content(mapper.writeValueAsBytes(caseDetailsToSave)) + ).andExpect(status().is(201)) + .andReturn(); + + assertNotNull(mvcResult); + + final String expectedLogMessage = "Adding missing field 'AddressLine2' under 'PersonAddress'."; + loggingEventList = listAppender.list; + + assertTrue("Expected log message not found: " + expectedLogMessage, + loggingEventList.stream().anyMatch(log -> log.getLevel() == logLevel + && log.getFormattedMessage().equals(expectedLogMessage))); + + final List caseDetailsList = template.query("SELECT * FROM case_data", this::mapCaseData); + assertEquals("Incorrect number of cases: No case should be created", NUMBER_OF_CASES, caseDetailsList.size()); + + final CaseDetails savedCaseDetails = caseDetailsList.stream() + .filter(c -> caseReference.equals(c.getReference().toString())) + .findFirst() + .orElse(null); + assertNotNull(savedCaseDetails); + assertEquals("Incorrect Case Type", CASE_TYPE_FILTERED, savedCaseDetails.getCaseTypeId()); + Map sanitizedDataMap = JacksonUtils.convertValue(sanitizedData); + assertThat( + "Incorrect Data content: Data should have changed", + savedCaseDetails.getData().entrySet(), + containsInAnyOrder(sanitizedDataMap.entrySet().toArray()) + ); + assertEquals("State should have been updated", "state4", savedCaseDetails.getState()); + JSONAssert.assertEquals(expectedClassificationString, + mapper.convertValue(savedCaseDetails.getDataClassification(), JsonNode.class).toString(), JSONCompareMode + .LENIENT); + + final List caseAuditEventList = template.query("SELECT * FROM case_event", this::mapAuditEvent); + assertEquals("A new event should have been created", 6, caseAuditEventList.size()); + + final AuditEvent caseAuditEvent = caseAuditEventList.getLast(); + assertAll( + () -> assertEquals(sanitizedDataMap, caseAuditEvent.getData()), + () -> assertEquals("123", caseAuditEvent.getUserId()), + () -> assertEquals("Strife", caseAuditEvent.getUserLastName()), + () -> assertEquals("Cloud", caseAuditEvent.getUserFirstName()), + () -> assertEquals("HAS PRE STATES EVENT", caseAuditEvent.getEventName()), + () -> assertEquals(savedCaseDetails.getId(), caseAuditEvent.getCaseDataId()), + () -> assertEquals(savedCaseDetails.getCaseTypeId(), caseAuditEvent.getCaseTypeId()), + () -> assertEquals(1, caseAuditEvent.getCaseTypeVersion().intValue()), + () -> assertEquals(savedCaseDetails.getState(), caseAuditEvent.getStateId()), + () -> assertEquals("Case in state 4", caseAuditEvent.getStateName()), + () -> assertEquals(savedCaseDetails.getData(), caseAuditEvent.getData()), + () -> assertEquals(SUMMARY, caseAuditEvent.getSummary()), + () -> assertEquals(DESCRIPTION, caseAuditEvent.getDescription()), + () -> JSONAssert.assertEquals(expectedClassificationString, + mapper.convertValue(caseAuditEvent.getDataClassification(), JsonNode.class).toString(), + JSONCompareMode.LENIENT) + ); + } + + @Test + @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_cases.sql"}) + public void shouldAddMissingCollectionFieldsBackToDataWhenReadFalseCreateTrueMatch() throws Exception { + final String caseReference = "6512245793128983"; + final String URL = + "/caseworkers/" + UID + "/jurisdictions/" + JURISDICTION + "/case-types/" + CASE_TYPE_FILTERED + + "/cases/" + caseReference + "/events"; + final CaseDataContent caseDetailsToSave = newCaseDataContent().build(); + caseDetailsToSave.setEvent(createEvent(PRE_STATES_EVENT_ID, SUMMARY, DESCRIPTION)); + + final JsonNode sanitizedData = mapper.readTree( + "{" + + "\"PersonAddress\":{" + + "\"Country\":\"_ Wales\"," + + "\"Postcode\":\"W11 5DF\"," + + "\"AddressLine1\":\"_ Flat 9\"," + + "\"AddressLine2\":\"Fake Street\"," + + "\"AddressLine3\":\"_ ButtonVillie\"}," + + "\"Aliases\":[" + + "{\"id\":\"2\",\"value\":\"Alias2\"}," + + "{\"id\":\"1\",\"value\":\"Alias1\"}," + + "{\"id\":\"3\",\"value\":\"Alias3\"}" + + "]," + + "\"PersonLastName\":\"_ Roof\"," + + "\"PersonFirstName\":\"_ George\"," + + "\"D8Document\":{" + + " \"document_url\": \"http://localhost:" + getPort() + + "/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0\"," + + " \"document_binary_url\": \"http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0" + + "/binary\"," + + " \"document_filename\": \"Seagulls_Square.jpg\"" + + "}" + + "}"); + + final JsonNode data = mapper.readTree(createExampleEventDataWithCollectionMissingItems()); + caseDetailsToSave.setData(JacksonUtils.convertValue(data)); + final String expectedClassificationString = "{" + + " \"PersonAddress\":{" + + " \"classification\": \"PUBLIC\"," + + " \"value\": {" + + " \"Country\":\"PUBLIC\"," + + " \"Postcode\":\"PUBLIC\"," + + " \"AddressLine1\":\"PUBLIC\"," + + " \"AddressLine3\":\"PUBLIC\"" + + " }" + + " }," + + " \"PersonLastName\":\"PUBLIC\"," + + " \"PersonFirstName\":\"PUBLIC\"," + + " \"D8Document\": \"PUBLIC\"" + + "}"; + final String token = generateEventToken(template, + UID, JURISDICTION, CASE_TYPE_FILTERED, caseReference, PRE_STATES_EVENT_ID); + caseDetailsToSave.setToken(token); + + final Level logLevel = Level.INFO; + + setupLogging().setLevel(logLevel); + + final MvcResult mvcResult = mockMvc.perform(post(URL) + .contentType(JSON_CONTENT_TYPE) + .content(mapper.writeValueAsBytes(caseDetailsToSave)) + ).andExpect(status().is(201)) + .andReturn(); + + assertNotNull(mvcResult); + + final List expectedLogMessages = List.of( + "Adding missing field 'AddressLine2' under 'PersonAddress'.", + "Adding missing collection item with ID '\"1\"' under 'Aliases'.", + "Adding missing collection item with ID '\"3\"' under 'Aliases'."); + loggingEventList = listAppender.list; + List actualLogMessages = loggingEventList.stream() + .filter(log -> log.getLevel() == logLevel) + .map(ILoggingEvent::getFormattedMessage) + .toList(); + + List missingMessages = expectedLogMessages.stream() + .filter(expected -> !actualLogMessages.contains(expected)) + .toList(); + assertTrue( + "The following expected log messages were not found: " + missingMessages, + missingMessages.isEmpty() + ); + List unexpectedMessages = actualLogMessages.stream() + .filter(actual -> !expectedLogMessages.contains(actual)) + .toList(); + assertTrue( + "The following unexpected log messages were found: " + unexpectedMessages, + unexpectedMessages.isEmpty() + ); + + final List caseDetailsList = template.query("SELECT * FROM case_data", this::mapCaseData); + assertEquals("Incorrect number of cases: No case should be created", NUMBER_OF_CASES, caseDetailsList.size()); + + final CaseDetails savedCaseDetails = caseDetailsList.stream() + .filter(c -> caseReference.equals(c.getReference().toString())) + .findFirst() + .orElse(null); + assertNotNull(savedCaseDetails); + assertEquals("Incorrect Case Type", CASE_TYPE_FILTERED, savedCaseDetails.getCaseTypeId()); + Map sanitizedDataMap = JacksonUtils.convertValue(sanitizedData); + assertThat( + "Incorrect Data content: Data should have changed", + savedCaseDetails.getData().entrySet(), + containsInAnyOrder(sanitizedDataMap.entrySet().toArray()) + ); + assertEquals("State should have been updated", "state4", savedCaseDetails.getState()); + JSONAssert.assertEquals(expectedClassificationString, + mapper.convertValue(savedCaseDetails.getDataClassification(), JsonNode.class).toString(), JSONCompareMode + .LENIENT); + + final List caseAuditEventList = template.query("SELECT * FROM case_event", this::mapAuditEvent); + assertEquals("A new event should have been created", 6, caseAuditEventList.size()); + + final AuditEvent caseAuditEvent = caseAuditEventList.getLast(); + assertAll( + () -> assertEquals(sanitizedDataMap, caseAuditEvent.getData()), + () -> assertEquals("123", caseAuditEvent.getUserId()), + () -> assertEquals("Cloud", caseAuditEvent.getUserFirstName()), + () -> assertEquals("HAS PRE STATES EVENT", caseAuditEvent.getEventName()), + () -> assertEquals(savedCaseDetails.getId(), caseAuditEvent.getCaseDataId()), + () -> assertEquals(savedCaseDetails.getCaseTypeId(), caseAuditEvent.getCaseTypeId()), + () -> assertEquals(1, caseAuditEvent.getCaseTypeVersion().intValue()), + () -> assertEquals(savedCaseDetails.getState(), caseAuditEvent.getStateId()), + () -> assertEquals("Case in state 4", caseAuditEvent.getStateName()), + () -> assertEquals(savedCaseDetails.getData(), caseAuditEvent.getData()), + () -> assertEquals(SUMMARY, caseAuditEvent.getSummary()), + () -> assertEquals(DESCRIPTION, caseAuditEvent.getDescription()), + () -> JSONAssert.assertEquals(expectedClassificationString, + mapper.convertValue(caseAuditEvent.getDataClassification(), JsonNode.class).toString(), + JSONCompareMode.LENIENT) + ); + } + + private String createExampleEventDataWithMissingItems() { + return "{" + + "\"PersonAddress\":{" + + "\"Country\":\"_ Wales\"," + + "\"Postcode\":\"W11 5DF\"," + + "\"AddressLine1\":\"_ Flat 9\"," + + "\"AddressLine3\":\"_ ButtonVillie\"}," + + "\"PersonLastName\":\"_ Roof\"," + + "\"PersonFirstName\":\"_ George\"," + + "\"D8Document\":{" + + "\"document_url\": \"http://localhost:" + getPort() + + "/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0\"" + + "}" + + "}"; + } + + private String createExampleEventDataWithCollectionMissingItems() { + return "{" + + "\"PersonAddress\":{" + + "\"Country\":\"_ Wales\"," + + "\"Postcode\":\"W11 5DF\"," + + "\"AddressLine1\":\"_ Flat 9\"," + + "\"AddressLine3\":\"_ ButtonVillie\"}," + + "\"PersonLastName\":\"_ Roof\"," + + "\"PersonFirstName\":\"_ George\"," + + "\"Aliases\":[" + + "{\"id\":\"2\",\"value\":\"Alias2\"}" + + "]," + + "\"D8Document\":{" + + "\"document_url\": \"http://localhost:" + getPort() + + "/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d0\"" + + "}" + + "}"; + } + private void assertCaseLinks(Long expectedCaseId, List expectedCaseLinks) { List caseLinks = template.query( String.format("SELECT * FROM case_link where case_id=%s", expectedCaseId), diff --git a/src/test/resources/mappings/bookcase-definition-cu-permission-filtered.json b/src/test/resources/mappings/bookcase-definition-cu-permission-filtered.json new file mode 100644 index 0000000000..141083c5c6 --- /dev/null +++ b/src/test/resources/mappings/bookcase-definition-cu-permission-filtered.json @@ -0,0 +1,612 @@ +{ + "request": { + "method": "GET", + "url": "/api/data/case-type/TestAddressBookCaseFiltered" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "id": "TestAddressBookCaseFiltered", + "version": { + "number": 1, + "live_from": "2017-01-01" + }, + "name": "Test Address Book Case", + "description": "Test Address Book Case", + "jurisdiction": { + "id": "PROBATE", + "name": "Test", + "description": "Test Jurisdiction" + }, + "security_classification": "PUBLIC", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-test-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "citizen", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "role-citizen", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "basic-profile", + "create": false, + "read": true, + "update": false, + "delete": false + } + ], + "roleToAccessProfiles": [ + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "caseworker-probate-public", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "idam:caseworker-probate-public", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "caseworker-probate-private", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "idam:caseworker-probate-private", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "caseworker-test-public", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "idam:caseworker-test-public", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "citizen", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "idam:citizen", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "role-citizen", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "idam:role-citizen", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "[CREATOR]", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "[CREATOR]", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "basic-profile", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "hmcts-legal-operations", + "case_access_categories": null + }, + { + "case_type_id": "TestAddressBookCaseFiltered", + "disabled": false, + "read_only": false, + "authorisations": null, + "access_profiles": "basic-profile", + "live_from": "2017-01-01", + "live_to": null, + "role_name": "hmcts-judiciary", + "case_access_categories": null + } + ], + "events": [ + { + "id": "HAS_PRE_STATES_EVENT", + "name": "HAS PRE STATES EVENT", + "description": "Test event for non null pre-states", + "order": 1, + "case_fields": [ + { + "case_field_id": "PersonFirstName", + "display_context": "READONLY", + "show_summary_content_option": 2 + }, + { + "case_field_id": "PersonLastName", + "display_context": "OPTIONAL", + "show_summary_content_option": 1 + } + ], + "pre_states": [ + "some-state", + "CaseCreated" + ], + "post_states": [{ + "enabling_condition" : null, + "priority" : 99, + "post_state_reference" : "state4" + }], + "security_classification": "PRIVATE", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": false, + "delete": false + }], + "show_event_notes": false, + "can_save_draft": false + }, + { + "id": "NO_PRE_STATES_EVENT", + "name": "NO PRE STATES EVENT", + "description": "Test event for null pre-states", + "order": 1, + "case_fields": [ + { + "case_field_id": "PersonFirstName", + "display_context": "READONLY" + }, + { + "case_field_id": "PersonLastName", + "display_context": "OPTIONAL" + } + ], + "pre_states": [ + ], + "post_states": [{ + "enabling_condition" : null, + "priority" : 99, + "post_state_reference" : "state4" + }], + "security_classification": "PRIVATE", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": false, + "delete": false + }], + "show_event_notes": true, + "can_save_draft": true + }, + { + "id": "TEST_EVENT", + "name": "TEST EVENT NAME", + "description": "Just a test", + "case_fields": [ + ], + "pre_states": [ + ], + "post_states": [{ + "enabling_condition" : null, + "priority" : 99, + "post_state_reference" : "state3" + }], + "security_classification": "PRIVATE", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": false, + "delete": false + }], + "show_event_notes": true, + "publish": true, + "can_save_draft": true + }, + { + "id": "Goodness", + "name": "GRACIOUS", + "description": "This is an event", + "order": 3, + "case_fields": [], + "pre_states": [ + "CaseCreated" + ], + "post_states": [{ + "enabling_condition" : null, + "priority" : 99, + "post_state_reference" : "state4" + }], + "security_classification": "PRIVATE", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": false, + "delete": false + }], + "show_event_notes": true, + "can_save_draft": false + }, + { + "id": "Create2", + "name": "CREATE_2", + "description": "Another creation event", + "order": 4, + "case_fields": [], + "pre_states": [], + "post_states": [{ + "enabling_condition" : null, + "priority" : 99, + "post_state_reference" : "state4" + }], + "security_classification": "PRIVATE", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": false, + "delete": false + }], + "show_event_notes": true, + "can_save_draft": true + } + ], + "states": [ + { + "id": "CaseCreated", + "name": "Case Created", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": false, + "read": true, + "update": true, + "delete": false + }, + { + "role": "role-citizen", + "create": false, + "read": true, + "update": true, + "delete": false + }, + { + "role": "basic-profile", + "create": false, + "read": true, + "update": false, + "delete": false + }] + }, + { + "id": "CaseEnteredIntoLegacy", + "name": "Case Has Been Entered Into Legacy", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": true, + "delete": false + }] + }, + { + "id": "CaseStopped", + "name": "Put case on hold", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": true, + "delete": false + }] + }, + { + "id": "state3", + "name": "Case in state 3", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": true, + "delete": false + }] + }, + { + "id": "state4", + "name": "Case in state 4", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": true, + "delete": false + }] + }, + { + "id": "some-state", + "name": "Case in some state", + "acls": [ + { + "role": "caseworker-probate-public", + "create": false, + "read": true, + "update": false, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": false, + "read": true, + "update": true, + "delete": false + }] + } + ], + "case_fields": [ + { + "id": "PersonFirstName", + "case_type_id": "TestAddressBookCaseFiltered", + "label": "First name", + "security_classification": "PUBLIC", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-test-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": true, + "read": true, + "update": true, + "delete": false + }], + "field_type": { + "type": "Text", + "id": "Text" + } + }, + { + "id": "PersonLastName", + "case_type_id": "TestAddressBookCaseFiltered", + "label": "Last name", + "security_classification": "PUBLIC", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-test-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": true, + "read": true, + "update": true, + "delete": false + }], + "field_type": { + "type": "Text", + "id": "Text" + } + }, + { + "id": "PersonAddress", + "case_type_id": "TestAddressBookCaseFiltered", + "label": "Address", + "security_classification": "PUBLIC", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": false, + "update": true, + "delete": false + }, + { + "role": "caseworker-test-public", + "create": true, + "read": true, + "update": true, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": true, + "read": true, + "update": true, + "delete": false + }], + "field_type": { + "id": "Address", + "type": "Complex", + "complex_fields": [ + { + "id": "AddressLine1", + "label": "Line1", + "security_classification": "PUBLIC", + "field_type": { + "id": "Text", + "type": "Text" + } + }, + { + "id": "AddressLine2", + "label": "Line 2", + "security_classification": "PUBLIC", + "field_type": { + "id": "Text", + "type": "Text" + } + }, + { + "id": "AddressLine3", + "label": "Line 3", + "security_classification": "PUBLIC", + "field_type": { + "id": "Text", + "type": "Text" + } + }, + { + "id": "Postcode", + "label": "Postcode", + "security_classification": "PUBLIC", + "field_type": { + "id": "Postcode", + "type": "Postcode" + } + }, + { + "id": "Country", + "label": "Country", + "security_classification": "PUBLIC", + "field_type": { + "id": "Text", + "type": "Text" + } + } + ] + } + }, + { + "id": "Aliases", + "case_type_id": "TestAddressBookCaseFiltered", + "label": "Aliases", + "security_classification": "PUBLIC", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": false, + "update": true, + "delete": true + }, + { + "role": "caseworker-probate-private", + "create": true, + "read": true, + "update": true, + "delete": false + }], + "field_type": { + "type": "Collection", + "id": "Collection", + "collection_field_type": { + "type": "Text", + "id": "Text" + } + } + }, + { + "id": "D8Document", + "case_type_id": "TestAddressBookCaseFiltered", + "label": "Document", + "security_classification": "PUBLIC", + "acls": [ + { + "role": "caseworker-probate-public", + "create": true, + "read": false, + "update": true, + "delete": false + }, + { + "role": "caseworker-probate-private", + "create": true, + "read": true, + "update": true, + "delete": false + }], + "field_type": { + "type": "Document", + "id": "Document" + } + } + ] + } + } +} diff --git a/src/test/resources/sql/insert_cases.sql b/src/test/resources/sql/insert_cases.sql index cc793788a9..7427dda841 100644 --- a/src/test/resources/sql/insert_cases.sql +++ b/src/test/resources/sql/insert_cases.sql @@ -1,6 +1,100 @@ DELETE FROM case_event; DELETE FROM case_data; +INSERT INTO case_data (id, case_type_id, jurisdiction, state, security_classification, data, data_classification, reference, created_date, last_modified, last_state_modified_date) +VALUES (25, 'TestAddressBookCaseFiltered', 'PROBATE', 'CaseCreated', 'PUBLIC', + '{ + "PersonFirstName": "Janet", + "PersonLastName": "Parker", + "PersonAddress": { + "AddressLine1": "123", + "AddressLine2": "Fake Street", + "AddressLine3": "Hexton", + "Country": "England", + "Postcode": "HX08 5TG" + }, + "Aliases": [ + { + "id": "1", + "value": "Alias1" + }, + { + "id": "2", + "value": "Alias2" + }, + { + "id": "3", + "value": "Alias3" + } + ], + "D8Document": { + "category_id": "detailsOfClaim", + "document_url": "http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d1", + "document_binary_url": "http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d1/binary", + "document_filename": "Seagulls_Square.jpg" + } + }', + '{ + "PersonFirstName": "PUBLIC", + "PersonLastName": "PUBLIC", + "PersonAddress": { + "classification" : "PUBLIC", + "value" : { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "Country": "PUBLIC", + "Postcode": "PUBLIC" + } + }, + "D8Document": "PUBLIC" + }', + '6512245793128983', + '2016-06-22 20:44:52.824', + '2016-06-24 20:44:52.824', + '2016-06-24 20:44:52.824' +); + +INSERT INTO case_data (id, case_type_id, jurisdiction, state, security_classification, data, data_classification, reference, created_date, last_modified, last_state_modified_date) +VALUES (24, 'TestAddressBookCaseFiltered', 'PROBATE', 'CaseCreated', 'PUBLIC', + '{ + "PersonFirstName": "Janet", + "PersonLastName": "Parker", + "PersonAddress": { + "AddressLine1": "123", + "AddressLine2": "Fake Street", + "AddressLine3": "Hexton", + "Country": "England", + "Postcode": "HX08 5TG" + }, + "D8Document": { + "category_id": "detailsOfClaim", + "document_url": "http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d1", + "document_binary_url": "http://localhost:[port]/documents/05e7cd7e-7041-4d8a-826a-7bb49dfd83d1/binary", + "document_filename": "Seagulls_Square.jpg" + } + }', + '{ + "PersonFirstName": "PUBLIC", + "PersonLastName": "PUBLIC", + "PersonAddress": { + "classification" : "PUBLIC", + "value" : { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "Country": "PUBLIC", + "Postcode": "PUBLIC" + } + }, + "D8Document": "PUBLIC" + }', + '1202264432028419', + '2016-06-22 20:44:52.824', + '2016-06-24 20:44:52.824', + '2016-06-24 20:44:52.824' +); + INSERT INTO case_data (id, case_type_id, jurisdiction, state, security_classification, data, data_classification, reference, created_date, last_modified, last_state_modified_date) VALUES (1, 'TestAddressBookCase', 'PROBATE', 'CaseCreated', 'PUBLIC', '{ From 8e0ac823099acc030de6d9bbb69c95365f3b00bb Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Fri, 20 Dec 2024 17:28:47 +0000 Subject: [PATCH 5/6] add F-038 functional test scenarios --- build.gradle | 2 +- .../F-038/F-038-Base-Prerequisite.td.json | 44 ++++++ ...ase-PrivateCaseWorker-Prerequisite.td.json | 44 ++++++ .../F-038/F-038-Case-Creation-Data.td.json | 100 +++++++++++++ .../F-038/F-038-Full-Case-Data.td.json | 64 ++++++++ ...F-038.VerifyMissingFieldsUnchanged.td.json | 139 ++++++++++++++++++ .../resources/features/F-038/F-038.feature | 95 ++++++++++++ .../F-038/F-038_Test_Data_Base.td.json | 60 ++++++++ .../resources/features/F-038/S-038.1.td.json | 109 ++++++++++++++ .../resources/features/F-038/S-038.2.td.json | 94 ++++++++++++ .../resources/features/F-038/S-038.3.td.json | 120 +++++++++++++++ .../resources/features/F-038/S-038.4.td.json | 113 ++++++++++++++ .../resources/features/F-038/S-038.5.td.json | 114 ++++++++++++++ 13 files changed, 1097 insertions(+), 1 deletion(-) create mode 100644 src/aat/resources/features/F-038/F-038-Base-Prerequisite.td.json create mode 100644 src/aat/resources/features/F-038/F-038-Base-PrivateCaseWorker-Prerequisite.td.json create mode 100644 src/aat/resources/features/F-038/F-038-Case-Creation-Data.td.json create mode 100644 src/aat/resources/features/F-038/F-038-Full-Case-Data.td.json create mode 100644 src/aat/resources/features/F-038/F-038.VerifyMissingFieldsUnchanged.td.json create mode 100644 src/aat/resources/features/F-038/F-038.feature create mode 100644 src/aat/resources/features/F-038/F-038_Test_Data_Base.td.json create mode 100644 src/aat/resources/features/F-038/S-038.1.td.json create mode 100644 src/aat/resources/features/F-038/S-038.2.td.json create mode 100644 src/aat/resources/features/F-038/S-038.3.td.json create mode 100644 src/aat/resources/features/F-038/S-038.4.td.json create mode 100644 src/aat/resources/features/F-038/S-038.5.td.json diff --git a/build.gradle b/build.gradle index 3d15376874..25982544f9 100644 --- a/build.gradle +++ b/build.gradle @@ -337,7 +337,7 @@ dependencies { implementation 'org.jooq:jool-java-8:0.9.14' implementation 'com.github.hmcts:ccd-case-document-am-client:1.7.1' - testImplementation group: 'com.github.hmcts', name: 'ccd-test-definitions', version: '7.24.0' + testImplementation group: 'com.github.hmcts', name: 'ccd-test-definitions', version: '7.24.3-prerelease-CCD-6022-1' testImplementation group: 'com.github.hmcts', name: 'befta-fw', version: '9.2.0' contractTestImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" diff --git a/src/aat/resources/features/F-038/F-038-Base-Prerequisite.td.json b/src/aat/resources/features/F-038/F-038-Base-Prerequisite.td.json new file mode 100644 index 0000000000..7012a8f78d --- /dev/null +++ b/src/aat/resources/features/F-038/F-038-Base-Prerequisite.td.json @@ -0,0 +1,44 @@ +{ + "_guid_": "F-038-Base-Prerequisite", + "title": "should create an event token for correct inputs", + + "productName": "CCD Data Store", + "operationName": "Retrieve a create token", + + "method": "GET", + "uri": "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/cases/{cid}/event-triggers/{etid}/token", + + "specs": [ + "to get an event token for the case just created" + ], + + "user": { + "_extends_": "Common_User_For_Request" + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "uid": "[[DEFAULT_AUTO_VALUE]]", + "jid": "AUTOTEST1", + "ctid": "AAT_AUTH_15", + "cid": "${[scenarioContext][parentContext][childContexts][Standard_Full_Case_Creation_Data][testData][actualResponse][body][id]}", + "etid": "UPDATE" + } + }, + + "expectedResponse": { + "_extends_": "Common_200_Response", + "headers": { + "Content-Encoding": "gzip", + "Content-Length": "[[ANYTHING_PRESENT]]" + }, + "body": { + "token": "[[ANYTHING_PRESENT]]", + "case_details": "[[ANYTHING_PRESENT]]", + "event_id": "UPDATE" + } + } +} diff --git a/src/aat/resources/features/F-038/F-038-Base-PrivateCaseWorker-Prerequisite.td.json b/src/aat/resources/features/F-038/F-038-Base-PrivateCaseWorker-Prerequisite.td.json new file mode 100644 index 0000000000..37fbf89cdd --- /dev/null +++ b/src/aat/resources/features/F-038/F-038-Base-PrivateCaseWorker-Prerequisite.td.json @@ -0,0 +1,44 @@ +{ + "_guid_": "F-038-Base-PrivateCaseWorker-Prerequisite", + "title": "should create an event token for correct inputs", + + "productName": "CCD Data Store", + "operationName": "Retrieve a create token", + + "method": "GET", + "uri": "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/cases/{cid}/event-triggers/{etid}/token", + + "specs": [ + "to get an event token for the case just created" + ], + + "user": { + "_extends_": "Common_User_For_Request" + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "uid": "[[DEFAULT_AUTO_VALUE]]", + "jid": "AUTOTEST1", + "ctid": "AAT_AUTH_15", + "cid": "${[scenarioContext][parentContext][childContexts][F-038-Case-Creation-Data][testData][actualResponse][body][id]}", + "etid": "UPDATE" + } + }, + + "expectedResponse": { + "_extends_": "Common_200_Response", + "headers": { + "Content-Encoding": "gzip", + "Content-Length": "[[ANYTHING_PRESENT]]" + }, + "body": { + "token": "[[ANYTHING_PRESENT]]", + "case_details": "[[ANYTHING_PRESENT]]", + "event_id": "UPDATE" + } + } +} diff --git a/src/aat/resources/features/F-038/F-038-Case-Creation-Data.td.json b/src/aat/resources/features/F-038/F-038-Case-Creation-Data.td.json new file mode 100644 index 0000000000..d3e119619b --- /dev/null +++ b/src/aat/resources/features/F-038/F-038-Case-Creation-Data.td.json @@ -0,0 +1,100 @@ +{ + "_guid_": "F-038-Case-Creation-Data", + "_extends_": "Case_Creation_Data_Base", + + "users": { + "invokingUser": { + "_extends_": "PrivateCaseworker" + } + }, + + "request": { + "pathVariables": { + "jid": "AUTOTEST1", + "ctid": "AAT_AUTH_15" + }, + "body": { + "data": { + "_extends_": "F-038-Full-Case-Data" + } + } + }, + + "expectedResponse": { + "body": { + "jurisdiction": "AUTOTEST1", + "case_type_id": "AAT_AUTH_15", + "case_data": { + "_extends_": "F-038-Full-Case-Data" + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "PostTown": "PUBLIC", + "County": "PUBLIC", + "PostCode": "PUBLIC", + "Country": "PUBLIC" + } + }, + "AddressForeignField": { + "classification": "PUBLIC", + "value": { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "PostTown": "PUBLIC", + "County": "PUBLIC", + "PostCode": "PUBLIC", + "Country": "PUBLIC" + } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": [ + { + "id": "CollectionField1", + "classification": "PUBLIC" + }, + { + "id": "CollectionField2", + "classification": "PUBLIC" + } + ] + }, + "CollectionPermissionField": { + "classification": "PUBLIC", + "value": [ + { + "id": "CollectionPermissionField1", + "classification": "PUBLIC" + }, + { + "id": "CollectionPermissionField2", + "classification": "PUBLIC" + }, + { + "id": "CollectionPermissionField3", + "classification": "PUBLIC" + } + ] + } + }, + "supplementary_data": null + } + } +} diff --git a/src/aat/resources/features/F-038/F-038-Full-Case-Data.td.json b/src/aat/resources/features/F-038/F-038-Full-Case-Data.td.json new file mode 100644 index 0000000000..4bcbd37074 --- /dev/null +++ b/src/aat/resources/features/F-038/F-038-Full-Case-Data.td.json @@ -0,0 +1,64 @@ +{ + "_guid_": "F-038-Full-Case-Data", + + "TextField": "Some Text", + "NumberField": "164528", + "YesOrNoField": "Yes", + "PhoneUKField": "07123456789", + "EmailField": "ccd@hmcts.net", + "MoneyGBPField": "4200", + "DateField": "2017-02-13", + "DateTimeField": "1988-07-07T22:20:00", + "TextAreaField": "Line1\nLine2", + "FixedListField": "VALUE3", + "MultiSelectListField": [ + "OPTION2", + "OPTION4" + ], + "CollectionField": [ + { + "id": "CollectionField1", + "value": "Alias 1" + }, + { + "id": "CollectionField2", + "value": "Alias 2" + } + ], + "CollectionPermissionField": [ + { + "id": "CollectionPermissionField1", + "value": "Alias 1" + }, + { + "id": "CollectionPermissionField2", + "value": "Alias 2" + }, + { + "id": "CollectionPermissionField3", + "value": "Alias 3" + } + ], + "ComplexField": { + "ComplexTextField": "Nested text", + "ComplexFixedListField": "VALUE2" + }, + "AddressUKField": { + "AddressLine1": "102 Petty France", + "AddressLine2": "CCD", + "AddressLine3": "c/o HMCTS Reform", + "PostTown": "Westminster", + "County": "Greater London", + "PostCode": "SW1H 9AJ", + "Country": "UK" + }, + "AddressForeignField": { + "AddressLine1": "102 Petty France", + "AddressLine2": "CCD", + "AddressLine3": "c/o HMCTS Reform", + "PostTown": "Westminster", + "County": "Paris", + "PostCode": "SW1H 9AJ", + "Country": "France" + } +} diff --git a/src/aat/resources/features/F-038/F-038.VerifyMissingFieldsUnchanged.td.json b/src/aat/resources/features/F-038/F-038.VerifyMissingFieldsUnchanged.td.json new file mode 100644 index 0000000000..089de2da3a --- /dev/null +++ b/src/aat/resources/features/F-038/F-038.VerifyMissingFieldsUnchanged.td.json @@ -0,0 +1,139 @@ +{ + "_guid_": "F-038.VerifyMissingFieldsUnchanged", + + "productName": "CCD Data Store", + "operationName": "Retrieve case data", + + "specs": [ + "to verify that the missing fields are unchanged" + ], + + "users": { + "invokingUser": { + "_extends_": "PrivateCaseworker" + } + }, + + "method": "GET", + "uri": "/cases/{cid}", + + "request": { + "headers": { + "_extends_": "Common_Request_Headers", + "experimental": "true" + }, + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][F-038-Case-Creation-Data][testData][actualResponse][body][id]}" + } + }, + + "expectedResponse": { + "_extends_": "Common_200_Response", + "headers": { + "Content-Encoding": "gzip", + "Content-Length": "[[ANY_INTEGER_NOT_NULLABLE]]" + }, + "body": { + "_links": { + "self": { + "href": "[[ANYTHING_PRESENT]]" + } + }, + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "[[ANYTHING_PRESENT]]", + "case_type": "[[ANYTHING_PRESENT]]", + "created_on": "[[ANYTHING_PRESENT]]", + "last_modified_on": "[[ANYTHING_PRESENT]]", + "last_state_modified_on" : "[[ANY_TIMESTAMP_NOT_NULLABLE]]", + "state": "[[ANYTHING_PRESENT]]", + "security_classification": "[[ANYTHING_PRESENT]]", + "data": { + "MoneyGBPField": "[[ANYTHING_PRESENT]]", + "FixedListField": "[[ANYTHING_PRESENT]]", + "AddressUKField": { + "County": "[[ANYTHING_PRESENT]]", + "Country": "[[ANYTHING_PRESENT]]", + "PostCode": "[[ANYTHING_PRESENT]]", + "PostTown": "[[ANYTHING_PRESENT]]", + "AddressLine1": "[[ANYTHING_PRESENT]]", + "AddressLine2": "[[ANYTHING_PRESENT]]", + "AddressLine3": "[[ANYTHING_PRESENT]]" + }, + "AddressForeignField": { + "County": "[[ANYTHING_PRESENT]]", + "Country": "[[ANYTHING_PRESENT]]", + "PostCode": "[[ANYTHING_PRESENT]]", + "PostTown": "[[ANYTHING_PRESENT]]", + "AddressLine1": "[[ANYTHING_PRESENT]]", + "AddressLine2": "[[ANYTHING_PRESENT]]", + "AddressLine3": "[[ANYTHING_PRESENT]]" + }, + "ComplexField": { + "ComplexTextField": "[[ANYTHING_PRESENT]]", + "ComplexFixedListField": "[[ANYTHING_PRESENT]]" + }, + "DateTimeField": "[[ANYTHING_PRESENT]]", + "PhoneUKField": "[[ANYTHING_PRESENT]]", + "NumberField": "[[ANYTHING_PRESENT]]", + "MultiSelectListField": "[[ANYTHING_PRESENT]]", + "YesOrNoField": "[[ANYTHING_PRESENT]]", + "EmailField": "[[ANYTHING_PRESENT]]", + "TextField": "[[ANYTHING_PRESENT]]", + "DateField": "[[ANYTHING_PRESENT]]", + "TextAreaField": "[[ANYTHING_PRESENT]]", + "CollectionField": "[[ANYTHING_PRESENT]]", + "CollectionPermissionField": "[[ANYTHING_PRESENT]]" + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "PostTown": "PUBLIC", + "County": "PUBLIC", + "PostCode": "PUBLIC", + "Country": "PUBLIC" + } + }, + "AddressForeignField": { + "classification": "PUBLIC", + "value": { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "PostTown": "PUBLIC", + "County": "PUBLIC", + "PostCode": "PUBLIC", + "Country": "PUBLIC" + } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + }, + "CollectionPermissionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + } + }, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-038/F-038.feature b/src/aat/resources/features/F-038/F-038.feature new file mode 100644 index 0000000000..4e247180f4 --- /dev/null +++ b/src/aat/resources/features/F-038/F-038.feature @@ -0,0 +1,95 @@ +#===================================================== +@F-038 +Feature: F-038: Validate field removal restrictions when submitting an event for an existing case (V2) +#===================================================== + +Background: Load test data for the scenario + Given an appropriate test context as detailed in the test data source + +#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +@S-038.1 +Scenario: must remove simple fields from AddressUKField if not sent in the event data, unless Read=False and Create=True + + Given a user with [an active profile in CCD], + And a case that has just been created as in [Standard_Full_Case_Creation_Data], + And a successful call [to get an event token for the case just created] as in [F-038-Base-Prerequisite], + + When a request is prepared with appropriate values, + And the request [contains a case Id that has just been created as in Standard_Full_Case_Creation_Data], + And the request [contains a token created as in F-038-Base-Prerequisite], + And it is submitted to call the [submit event for an existing case (V2)] operation of [CCD data store], + + Then a positive response is received, + And the response [contains the case detail for the updated case, along with a HTTP 200 OK], + And the response has all other details as expected. + +#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +@S-038.2 +Scenario: must remove complex AddressUKField if sent null in the event data, unless Read=False and Create=True + + Given a user with [an active profile in CCD], + And a case that has just been created as in [Standard_Full_Case_Creation_Data], + And a successful call [to get an event token for the case just created] as in [F-038-Base-Prerequisite], + + When a request is prepared with appropriate values, + And the request [contains a case Id that has just been created as in Standard_Full_Case_Creation_Data], + And the request [contains a token created as in F-038-Base-Prerequisite], + And it is submitted to call the [submit event for an existing case (V2)] operation of [CCD data store], + + Then a positive response is received, + And the response [contains the case detail for the updated case, along with a HTTP 200 OK], + And the response has all other details as expected. + +#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + @S-038.3 + Scenario: must remove collection if not sent in the event data, unless Read=False and Create=True + + Given a user with [an active profile in CCD], + And a case that has just been created as in [Standard_Full_Case_Creation_Data], + And a successful call [to get an event token for the case just created] as in [F-038-Base-Prerequisite], + + When a request is prepared with appropriate values, + And the request [contains a case Id that has just been created as in Standard_Full_Case_Creation_Data], + And the request [contains a token created as in F-038-Base-Prerequisite], + And it is submitted to call the [submit event for an existing case (V2)] operation of [CCD data store], + + Then a positive response is received, + And the response [contains the case detail for the updated case, along with a HTTP 200 OK], + And the response has all other details as expected. + +#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + @S-038.4 + Scenario: must add simple fields from AddressForeignField if not sent in the event data, if Read=False and Create=True + + Given a user with [an active profile in CCD], + And a case that has just been created as in [F-038-Case-Creation-Data], + And a successful call [to get an event token for the case just created] as in [F-038-Base-PrivateCaseWorker-Prerequisite], + + When a request is prepared with appropriate values, + And the request [contains a case Id that has just been created as in F-038-Case-Creation-Data], + And the request [contains a token created as in F-038-Base-PrivateCaseWorker-Prerequisite], + And it is submitted to call the [submit event for an existing case (V2)] operation of [CCD data store], + + Then a positive response is received, + And the response [contains the case detail for the updated case, along with a HTTP 201 OK], + And the response has all other details as expected. + And another call [to verify that the missing fields are unchanged] will get the expected response as in [F-038.VerifyMissingFieldsUnchanged] + +#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + @S-038.5 + Scenario: must add collection items from CollectionPermissionField if not sent in the event data, if Read=False and + Create=True + + Given a user with [an active profile in CCD], + And a case that has just been created as in [F-038-Case-Creation-Data], + And a successful call [to get an event token for the case just created] as in [F-038-Base-PrivateCaseWorker-Prerequisite], + + When a request is prepared with appropriate values, + And the request [contains a case Id that has just been created as in F-038-Case-Creation-Data], + And the request [contains a token created as in F-038-Base-PrivateCaseWorker-Prerequisite], + And it is submitted to call the [submit event for an existing case (V2)] operation of [CCD data store], + + Then a positive response is received, + And the response [contains the case detail for the updated case, along with a HTTP 201 OK], + And the response has all other details as expected. + And another call [to verify that the missing fields are unchanged] will get the expected response as in [F-038.VerifyMissingFieldsUnchanged] diff --git a/src/aat/resources/features/F-038/F-038_Test_Data_Base.td.json b/src/aat/resources/features/F-038/F-038_Test_Data_Base.td.json new file mode 100644 index 0000000000..c20aa29f83 --- /dev/null +++ b/src/aat/resources/features/F-038/F-038_Test_Data_Base.td.json @@ -0,0 +1,60 @@ +{ + "_guid_": "F-038_Test_Data_Base", + + "productName": "CCD data store", + "operationName": "submit event for an existing case (V2)", + + "method": "POST", + "uri": "/cases/{cid}/events", + + "user": { + "_extends_": "Common_User_For_Request" + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers", + "Content-Type": "application/vnd.uk.gov.hmcts.ccd-data-store-api.create-event.v2+json", + "experimental": true + }, + "pathVariables": { + "cid": "1234567890123456" + }, + "body": { + "data": { + "MoneyGBPField": "4200", + "FixedListField": "VALUE3", + "ComplexField": { + "ComplexTextField": "Nested text", + "ComplexFixedListField": "VALUE2" + }, + "DateTimeField": "1988-07-07T22:20:00", + "PhoneUKField": "07123456789", + "NumberField": "164528", + "MultiSelectListField": [ + "OPTION2", + "OPTION4" + ], + "YesOrNoField": "Yes", + "EmailField": "ccd@hmcts.net", + "TextField": "Some Text", + "DateField": "2017-02-13", + "TextAreaField": "Line1\nLine2" + }, + "event": { + "id": "UPDATE", + "summary": "", + "description": "" + }, + "event_token": "testToken", + "ignore_warning": false + } + }, + + "expectedResponse": { + "headers": { + "Content-Length": "[[ANYTHING_PRESENT]]", + "Content-Encoding" : "gzip" + } + } +} diff --git a/src/aat/resources/features/F-038/S-038.1.td.json b/src/aat/resources/features/F-038/S-038.1.td.json new file mode 100644 index 0000000000..3b2bb01515 --- /dev/null +++ b/src/aat/resources/features/F-038/S-038.1.td.json @@ -0,0 +1,109 @@ +{ + "_guid_": "S-038.1", + "_extends_": "F-038_Test_Data_Base", + "title": "should create event successfully for an existing case", + + "specs": [ + "an active profile in CCD", + "contains a case Id that has just been created as in Standard_Full_Case_Creation_Data", + "contains a token created as in F-038-Base-Prerequisite", + "contains the case detail for the updated case, along with a HTTP 200 OK" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][childContexts][Standard_Full_Case_Creation_Data][testData][actualResponse][body][id]}" + }, + "body": { + "data": { + "AddressUKField": { + "PostTown": "Westminster", + "AddressLine1": "102 Petty France", + "AddressLine2": "CCD", + "AddressLine3": "c/o HMCTS Reform" + } + }, + "event_token": "${[scenarioContext][childContexts][F-038-Base-Prerequisite][testData][actualResponse][body][token]}" + } + }, + + "expectedResponse": { + "responseCode": 201, + "responseMessage": "OK", + "headers": { + "_extends_": "Common_Response_Headers", + "Content-Type": "application/vnd.uk.gov.hmcts.ccd-data-store-api.create-event.v2+json;charset=UTF-8", + "Vary" : "Accept-Encoding" + }, + "body": { + "_links": { + "self": { + "href": "[[ANYTHING_PRESENT]]" + } + }, + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "[[ANYTHING_PRESENT]]", + "case_type": "[[ANYTHING_PRESENT]]", + "created_on": "[[ANYTHING_PRESENT]]", + "last_modified_on": "[[ANYTHING_PRESENT]]", + "last_state_modified_on" : "[[ANY_TIMESTAMP_NOT_NULLABLE]]", + "state": "[[ANYTHING_PRESENT]]", + "security_classification": "[[ANYTHING_PRESENT]]", + "data": { + "MoneyGBPField": "[[ANYTHING_PRESENT]]", + "FixedListField": "[[ANYTHING_PRESENT]]", + "AddressUKField": { + "PostTown": "[[ANYTHING_PRESENT]]", + "AddressLine1": "[[ANYTHING_PRESENT]]", + "AddressLine2": "[[ANYTHING_PRESENT]]", + "AddressLine3": "[[ANYTHING_PRESENT]]" + }, + "ComplexField": { + "ComplexTextField": "[[ANYTHING_PRESENT]]", + "ComplexFixedListField": "[[ANYTHING_PRESENT]]" + }, + "DateTimeField": "[[ANYTHING_PRESENT]]", + "PhoneUKField": "[[ANYTHING_PRESENT]]", + "NumberField": "[[ANYTHING_PRESENT]]", + "MultiSelectListField": "[[ANYTHING_PRESENT]]", + "YesOrNoField": "[[ANYTHING_PRESENT]]", + "EmailField": "[[ANYTHING_PRESENT]]", + "TextField": "[[ANYTHING_PRESENT]]", + "DateField": "[[ANYTHING_PRESENT]]", + "TextAreaField": "[[ANYTHING_PRESENT]]", + "CollectionField": "[[ANYTHING_PRESENT]]" + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { + "PostTown": "PUBLIC", + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC" + } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + } + }, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-038/S-038.2.td.json b/src/aat/resources/features/F-038/S-038.2.td.json new file mode 100644 index 0000000000..b3be2e9e6e --- /dev/null +++ b/src/aat/resources/features/F-038/S-038.2.td.json @@ -0,0 +1,94 @@ +{ + "_guid_": "S-038.2", + "_extends_": "F-038_Test_Data_Base", + "title": "should create event successfully for an existing case", + + "specs": [ + "an active profile in CCD", + "contains a case Id that has just been created as in Standard_Full_Case_Creation_Data", + "contains a token created as in F-038-Base-Prerequisite", + "contains the case detail for the updated case, along with a HTTP 200 OK" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][childContexts][Standard_Full_Case_Creation_Data][testData][actualResponse][body][id]}" + }, + "body": { + "data": { + "AddressUKField": null + }, + "event_token": "${[scenarioContext][childContexts][F-038-Base-Prerequisite][testData][actualResponse][body][token]}" + } + }, + + "expectedResponse": { + "responseCode": 201, + "responseMessage": "OK", + "headers": { + "_extends_": "Common_Response_Headers", + "Content-Type": "application/vnd.uk.gov.hmcts.ccd-data-store-api.create-event.v2+json;charset=UTF-8", + "Vary" : "Accept-Encoding" + }, + "body": { + "_links": { + "self": { + "href": "[[ANYTHING_PRESENT]]" + } + }, + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "[[ANYTHING_PRESENT]]", + "case_type": "[[ANYTHING_PRESENT]]", + "created_on": "[[ANYTHING_PRESENT]]", + "last_modified_on": "[[ANYTHING_PRESENT]]", + "last_state_modified_on" : "[[ANY_TIMESTAMP_NOT_NULLABLE]]", + "state": "[[ANYTHING_PRESENT]]", + "security_classification": "[[ANYTHING_PRESENT]]", + "data": { + "MoneyGBPField": "[[ANYTHING_PRESENT]]", + "FixedListField": "[[ANYTHING_PRESENT]]", + "AddressUKField": "[[ANYTHING_PRESENT]]", + "ComplexField": { + "ComplexTextField": "[[ANYTHING_PRESENT]]", + "ComplexFixedListField": "[[ANYTHING_PRESENT]]" + }, + "DateTimeField": "[[ANYTHING_PRESENT]]", + "PhoneUKField": "[[ANYTHING_PRESENT]]", + "NumberField": "[[ANYTHING_PRESENT]]", + "MultiSelectListField": "[[ANYTHING_PRESENT]]", + "YesOrNoField": "[[ANYTHING_PRESENT]]", + "EmailField": "[[ANYTHING_PRESENT]]", + "TextField": "[[ANYTHING_PRESENT]]", + "DateField": "[[ANYTHING_PRESENT]]", + "TextAreaField": "[[ANYTHING_PRESENT]]", + "CollectionField": "[[ANYTHING_PRESENT]]" + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + } + }, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-038/S-038.3.td.json b/src/aat/resources/features/F-038/S-038.3.td.json new file mode 100644 index 0000000000..b959241cfa --- /dev/null +++ b/src/aat/resources/features/F-038/S-038.3.td.json @@ -0,0 +1,120 @@ +{ + "_guid_": "S-038.3", + "_extends_": "F-038_Test_Data_Base", + "title": "should create event successfully for an existing case", + + "specs": [ + "an active profile in CCD", + "contains a case Id that has just been created as in Standard_Full_Case_Creation_Data", + "contains a token created as in F-038-Base-Prerequisite", + "contains the case detail for the updated case, along with a HTTP 200 OK" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][childContexts][Standard_Full_Case_Creation_Data][testData][actualResponse][body][id]}" + }, + "body": { + "data": { + "CollectionField": [ + { + "id": "936b6a65-a179-474f-bdff-1d4f961e9a8c", + "value": "Alias 2" + } + ] + }, + "event_token": "${[scenarioContext][childContexts][F-038-Base-Prerequisite][testData][actualResponse][body][token]}" + } + }, + + "expectedResponse": { + "responseCode": 201, + "responseMessage": "OK", + "headers": { + "_extends_": "Common_Response_Headers", + "Content-Type": "application/vnd.uk.gov.hmcts.ccd-data-store-api.create-event.v2+json;charset=UTF-8", + "Vary" : "Accept-Encoding" + }, + "body": { + "_links": { + "self": { + "href": "[[ANYTHING_PRESENT]]" + } + }, + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "[[ANYTHING_PRESENT]]", + "case_type": "[[ANYTHING_PRESENT]]", + "created_on": "[[ANYTHING_PRESENT]]", + "last_modified_on": "[[ANYTHING_PRESENT]]", + "last_state_modified_on" : "[[ANY_TIMESTAMP_NOT_NULLABLE]]", + "state": "[[ANYTHING_PRESENT]]", + "security_classification": "[[ANYTHING_PRESENT]]", + "data": { + "MoneyGBPField": "[[ANYTHING_PRESENT]]", + "FixedListField": "[[ANYTHING_PRESENT]]", + "AddressUKField": { + "County": "Greater London", + "Country": "UK", + "PostCode": "SW1H 9AJ", + "PostTown": "Westminster", + "AddressLine1": "102 Petty France", + "AddressLine2": "CCD", + "AddressLine3": "c/o HMCTS Reform" + }, + "ComplexField": { + "ComplexTextField": "[[ANYTHING_PRESENT]]", + "ComplexFixedListField": "[[ANYTHING_PRESENT]]" + }, + "DateTimeField": "[[ANYTHING_PRESENT]]", + "PhoneUKField": "[[ANYTHING_PRESENT]]", + "NumberField": "[[ANYTHING_PRESENT]]", + "MultiSelectListField": "[[ANYTHING_PRESENT]]", + "YesOrNoField": "[[ANYTHING_PRESENT]]", + "EmailField": "[[ANYTHING_PRESENT]]", + "TextField": "[[ANYTHING_PRESENT]]", + "DateField": "[[ANYTHING_PRESENT]]", + "TextAreaField": "[[ANYTHING_PRESENT]]", + "CollectionField": [ + { + "id": "936b6a65-a179-474f-bdff-1d4f961e9a8c", + "value": "Alias 2" + } + ] + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { + "County": "PUBLIC", + "Country": "PUBLIC", + "PostCode": "PUBLIC", + "PostTown": "PUBLIC", + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC" + } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + } + }, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-038/S-038.4.td.json b/src/aat/resources/features/F-038/S-038.4.td.json new file mode 100644 index 0000000000..11a56ccc5b --- /dev/null +++ b/src/aat/resources/features/F-038/S-038.4.td.json @@ -0,0 +1,113 @@ +{ + "_guid_": "S-038.4", + "_extends_": "F-038_Test_Data_Base", + "title": "should create event successfully for an existing case", + + "specs": [ + "an active profile in CCD", + "contains a case Id that has just been created as in F-038-Case-Creation-Data", + "contains a token created as in F-038-Base-PrivateCaseWorker-Prerequisite", + "contains the case detail for the updated case, along with a HTTP 201 OK" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][childContexts][F-038-Case-Creation-Data][testData][actualResponse][body][id]}" + }, + "body": { + "data": { + "AddressForeignField": { + "AddressLine1": "102 Petty France", + "PostCode": "SW1H 9AJ", + "Country": "France" + } + }, + "event_token": "${[scenarioContext][childContexts][F-038-Base-PrivateCaseWorker-Prerequisite][testData][actualResponse][body][token]}" + } + }, + + "expectedResponse": { + "responseCode": 201, + "responseMessage": "OK", + "headers": { + "_extends_": "Common_Response_Headers", + "Vary" : "Accept-Encoding" + }, + "body": { + "_links": { + "self": { + "href": "[[ANYTHING_PRESENT]]" + } + }, + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "[[ANYTHING_PRESENT]]", + "case_type": "[[ANYTHING_PRESENT]]", + "created_on": "[[ANYTHING_PRESENT]]", + "last_modified_on": "[[ANYTHING_PRESENT]]", + "last_state_modified_on" : "[[ANY_TIMESTAMP_NOT_NULLABLE]]", + "state": "[[ANYTHING_PRESENT]]", + "security_classification": "[[ANYTHING_PRESENT]]", + "data": { + "MoneyGBPField": "[[ANYTHING_PRESENT]]", + "FixedListField": "[[ANYTHING_PRESENT]]", + "ComplexField": { + "ComplexTextField": "[[ANYTHING_PRESENT]]", + "ComplexFixedListField": "[[ANYTHING_PRESENT]]" + }, + "AddressUKField": { + "AddressLine1": "[[ANYTHING_PRESENT]]", + "AddressLine2": "[[ANYTHING_PRESENT]]", + "AddressLine3": "[[ANYTHING_PRESENT]]", + "PostTown": "[[ANYTHING_PRESENT]]", + "County": "[[ANYTHING_PRESENT]]", + "PostCode": "[[ANYTHING_PRESENT]]", + "Country": "[[ANYTHING_PRESENT]]" + }, + "DateTimeField": "[[ANYTHING_PRESENT]]", + "PhoneUKField": "[[ANYTHING_PRESENT]]", + "NumberField": "[[ANYTHING_PRESENT]]", + "MultiSelectListField": "[[ANYTHING_PRESENT]]", + "YesOrNoField": "[[ANYTHING_PRESENT]]", + "EmailField": "[[ANYTHING_PRESENT]]", + "TextField": "[[ANYTHING_PRESENT]]", + "DateField": "[[ANYTHING_PRESENT]]", + "TextAreaField": "[[ANYTHING_PRESENT]]", + "CollectionField": "[[ANYTHING_PRESENT]]" + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "PostTown": "PUBLIC", + "County": "PUBLIC", + "PostCode": "PUBLIC", + "Country": "PUBLIC" + } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + } + }, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-038/S-038.5.td.json b/src/aat/resources/features/F-038/S-038.5.td.json new file mode 100644 index 0000000000..f2a484e860 --- /dev/null +++ b/src/aat/resources/features/F-038/S-038.5.td.json @@ -0,0 +1,114 @@ +{ + "_guid_": "S-038.5", + "_extends_": "F-038_Test_Data_Base", + "title": "should create event successfully for an existing case", + + "specs": [ + "an active profile in CCD", + "contains a case Id that has just been created as in F-038-Case-Creation-Data", + "contains a token created as in F-038-Base-PrivateCaseWorker-Prerequisite", + "contains the case detail for the updated case, along with a HTTP 201 OK" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][childContexts][F-038-Case-Creation-Data][testData][actualResponse][body][id]}" + }, + "body": { + "data": { + "CollectionPermissionField": [ + { + "id": "CollectionPermissionField3", + "value": "Alias 3" + } + ] + }, + "event_token": "${[scenarioContext][childContexts][F-038-Base-PrivateCaseWorker-Prerequisite][testData][actualResponse][body][token]}" + } + }, + + "expectedResponse": { + "responseCode": 201, + "responseMessage": "OK", + "headers": { + "_extends_": "Common_Response_Headers", + "Vary" : "Accept-Encoding" + }, + "body": { + "_links": { + "self": { + "href": "[[ANYTHING_PRESENT]]" + } + }, + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "[[ANYTHING_PRESENT]]", + "case_type": "[[ANYTHING_PRESENT]]", + "created_on": "[[ANYTHING_PRESENT]]", + "last_modified_on": "[[ANYTHING_PRESENT]]", + "last_state_modified_on" : "[[ANY_TIMESTAMP_NOT_NULLABLE]]", + "state": "[[ANYTHING_PRESENT]]", + "security_classification": "[[ANYTHING_PRESENT]]", + "data": { + "MoneyGBPField": "[[ANYTHING_PRESENT]]", + "FixedListField": "[[ANYTHING_PRESENT]]", + "ComplexField": { + "ComplexTextField": "[[ANYTHING_PRESENT]]", + "ComplexFixedListField": "[[ANYTHING_PRESENT]]" + }, + "AddressUKField": { + "AddressLine1": "[[ANYTHING_PRESENT]]", + "AddressLine2": "[[ANYTHING_PRESENT]]", + "AddressLine3": "[[ANYTHING_PRESENT]]", + "PostTown": "[[ANYTHING_PRESENT]]", + "County": "[[ANYTHING_PRESENT]]", + "PostCode": "[[ANYTHING_PRESENT]]", + "Country": "[[ANYTHING_PRESENT]]" + }, + "DateTimeField": "[[ANYTHING_PRESENT]]", + "PhoneUKField": "[[ANYTHING_PRESENT]]", + "NumberField": "[[ANYTHING_PRESENT]]", + "MultiSelectListField": "[[ANYTHING_PRESENT]]", + "YesOrNoField": "[[ANYTHING_PRESENT]]", + "EmailField": "[[ANYTHING_PRESENT]]", + "TextField": "[[ANYTHING_PRESENT]]", + "DateField": "[[ANYTHING_PRESENT]]", + "TextAreaField": "[[ANYTHING_PRESENT]]", + "CollectionField": "[[ANYTHING_PRESENT]]" + }, + "data_classification": { + "MoneyGBPField": "PUBLIC", + "FixedListField": "PUBLIC", + "AddressUKField": { + "classification": "PUBLIC", + "value": { + "AddressLine1": "PUBLIC", + "AddressLine2": "PUBLIC", + "AddressLine3": "PUBLIC", + "PostTown": "PUBLIC", + "County": "PUBLIC", + "PostCode": "PUBLIC", + "Country": "PUBLIC" + } + }, + "DateTimeField": "PUBLIC", + "PhoneUKField": "PUBLIC", + "NumberField": "PUBLIC", + "MultiSelectListField": "PUBLIC", + "YesOrNoField": "PUBLIC", + "EmailField": "PUBLIC", + "TextField": "PUBLIC", + "DateField": "PUBLIC", + "TextAreaField": "PUBLIC", + "CollectionField": { + "classification": "PUBLIC", + "value": "[[ANYTHING_PRESENT]]" + } + }, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} From 2c74cc664724de40be8f927a5cd336ff6d22eff8 Mon Sep 17 00:00:00 2001 From: kaanaktas Date: Mon, 30 Dec 2024 11:18:13 +0000 Subject: [PATCH 6/6] update ccd-test-definitions to 7.24.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 25982544f9..1d233bc1d0 100644 --- a/build.gradle +++ b/build.gradle @@ -337,7 +337,7 @@ dependencies { implementation 'org.jooq:jool-java-8:0.9.14' implementation 'com.github.hmcts:ccd-case-document-am-client:1.7.1' - testImplementation group: 'com.github.hmcts', name: 'ccd-test-definitions', version: '7.24.3-prerelease-CCD-6022-1' + testImplementation group: 'com.github.hmcts', name: 'ccd-test-definitions', version: '7.24.2' testImplementation group: 'com.github.hmcts', name: 'befta-fw', version: '9.2.0' contractTestImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}"