From 08b8eaf03a7903752128e588297c14303b196bfc Mon Sep 17 00:00:00 2001 From: leventecsanyi Date: Fri, 14 Nov 2025 15:54:09 +0100 Subject: [PATCH] Fix casting issue for Patch operation - fixed casting issue for Resource - refactored mapping logic for patch item Issue-ID: CPS-3055 Change-Id: Ia471e53bde656bb7865aee6616f1ad57e44f82bf Signed-off-by: leventecsanyi --- .../impl/data/policyexecutor/PolicyExecutor.java | 61 ++++++++++++---------- .../data/policyexecutor/PolicyExecutorSpec.groovy | 9 ++-- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java index d0d397f81f..8e21f9cae6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java @@ -28,7 +28,6 @@ import java.net.UnknownHostException; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -43,7 +42,6 @@ import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.ncmp.impl.provmns.RequestPathParameters; import org.onap.cps.ncmp.impl.provmns.model.PatchItem; -import org.onap.cps.ncmp.impl.provmns.model.Resource; import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; import org.onap.cps.utils.JsonObjectMapper; @@ -91,6 +89,8 @@ public class PolicyExecutor { private final JsonObjectMapper jsonObjectMapper; private static final Throwable NO_ERROR = null; + private static final String ATTRIBUTE_NAME_SEPARATOR = "/"; + private static final String REGEX_FOR_LEADING_AND_TRAILING_SEPARATORS = "(^/)|(/$)"; /** * Use the Policy Executor to check permission for a cm write operation. @@ -141,7 +141,7 @@ public class PolicyExecutor { switch (patchItem.getOp()) { case ADD -> operations.add( buildCreateOperationDetails(OperationType.CREATE, requestPathParameters, - (Resource) patchItem.getValue())); + patchItem.getValue())); case REPLACE -> operations.add( buildCreateOperationDetailsForUpdate(OperationType.UPDATE, requestPathParameters, patchItem)); case REMOVE -> operations.add( @@ -157,26 +157,26 @@ public class PolicyExecutor { * * @param operationType Type of operation create, update. * @param requestPathParameters request parameters including uri-ldn-first-part, className and id - * @param resource provided request resource + * @param resourceAsObject provided request payload * @return CreateOperationDetails object */ public CreateOperationDetails buildCreateOperationDetails(final OperationType operationType, final RequestPathParameters requestPathParameters, - final Resource resource) { + final Object resourceAsObject) { final Map> changeRequest = new HashMap<>(); final OperationEntry operationEntry = new OperationEntry(); - final String resourceAsJson = jsonObjectMapper.asJsonString(resource); + final String resourceAsJson = jsonObjectMapper.asJsonString(resourceAsObject); String className = requestPathParameters.getClassName(); try { final TypeReference> typeReference = new TypeReference>() {}; - final Map fullValue = objectMapper.readValue(resourceAsJson, typeReference); + final Map valueMap = objectMapper.readValue(resourceAsJson, typeReference); operationEntry.setId(requestPathParameters.getId()); - operationEntry.setAttributes(fullValue.get("attributes")); - className = isNullEmptyOrBlank(fullValue) - ? requestPathParameters.getClassName() : fullValue.get("objectClass").toString(); + operationEntry.setAttributes(valueMap.get("attributes")); + className = isNullEmptyOrBlank(valueMap) + ? requestPathParameters.getClassName() : valueMap.get("objectClass").toString(); } catch (final JsonProcessingException exception) { log.debug("JSON processing error: {}", exception); } @@ -199,7 +199,7 @@ public class PolicyExecutor { if (patchItem.getPath().contains(ATTRIBUTES_WITH_HASHTAG)) { return buildCreateOperationDetailsForUpdateWithHash(operationType, requestPathParameters, patchItem); } else { - return buildCreateOperationDetails(operationType, requestPathParameters, (Resource) patchItem.getValue()); + return buildCreateOperationDetails(operationType, requestPathParameters, patchItem.getValue()); } } @@ -210,10 +210,10 @@ public class PolicyExecutor { final OperationEntry operationEntry = new OperationEntry(); final String className = requestPathParameters.getClassName(); - final Map attributeHiearchyAsMap = getAttributeHierarchyMap(patchItem); + final Map attributeHierarchyAsMap = createNestedMap(patchItem); operationEntry.setId(requestPathParameters.getId()); - operationEntry.setAttributes(attributeHiearchyAsMap); + operationEntry.setAttributes(attributeHierarchyAsMap); changeRequest.put(className, List.of(operationEntry)); return new CreateOperationDetails(operationType.getOperationName(), @@ -221,25 +221,26 @@ public class PolicyExecutor { changeRequest); } - private Map getAttributeHierarchyMap(final PatchItem patchItem) { - final String[] parts = patchItem.getPath().split(ATTRIBUTES_WITH_HASHTAG); + private Map createNestedMap(final PatchItem patchItem) { + final Map attributeHierarchyMap = new HashMap<>(); + Map currentLevel = attributeHierarchyMap; - final String attributeHierarchy = parts[1]; - final String[] attributeHierarchyAsArray = Arrays.stream(attributeHierarchy.split("/")) - .filter(attributeName -> !attributeName.isEmpty()) - .toArray(String[]::new); + final String[] attributeHierarchyNames = patchItem.getPath().split(ATTRIBUTES_WITH_HASHTAG)[1] + .replaceAll(REGEX_FOR_LEADING_AND_TRAILING_SEPARATORS, "") + .split(ATTRIBUTE_NAME_SEPARATOR); - return buildAttributeHiearchyAsMap(attributeHierarchyAsArray, 0, patchItem.getValue()); - } + for (int level = 0; level < attributeHierarchyNames.length; level++) { + final String attributeName = attributeHierarchyNames[level]; - private Map buildAttributeHiearchyAsMap(final String[] parts, - final int index, - final Object value) { - if (index == parts.length - 1) { - return Map.of(parts[index], value); + if (isLastLevel(attributeHierarchyNames, level)) { + currentLevel.put(attributeName, patchItem.getValue()); + } else { + final Map nextLevel = new HashMap<>(); + currentLevel.put(attributeName, nextLevel); + currentLevel = nextLevel; + } } - - return Map.of(parts[index], buildAttributeHiearchyAsMap(parts, index + 1, value)); + return attributeHierarchyMap; } /** @@ -385,4 +386,8 @@ public class PolicyExecutor { return true; } } + + private boolean isLastLevel(final String[] attributeNamesArray, final int level) { + return level == attributeNamesArray.length - 1; + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy index a71ddf4434..d05165740f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy @@ -281,13 +281,14 @@ class PolicyExecutorSpec extends Specification { given: 'a patch item with a path' def patchItem = new PatchItem(op: 'REPLACE', 'path':path, value: 123) when: 'transforming the attributes' - def hierarchyMap = objectUnderTest.getAttributeHierarchyMap(patchItem) + def hierarchyMap = objectUnderTest.createNestedMap(patchItem) then: 'the map depth is equal to the expected number of attributes' assert hierarchyMap.get(expectedAttributeName).toString() == expectedAttributeValue where: 'simple and complex attributes are tested' - scenario | path || expectedAttributeName || expectedAttributeValue - 'set a simple attribute' | 'myUriLdnFirstPart#/attributes/simpleAttribute' || 'simpleAttribute' || '123' - 'set a complex attribute' | 'myUriLdnFirstPart#/attributes/complexAttribute/simpleAttribute' || 'complexAttribute' || '[simpleAttribute:123]' + scenario | path || expectedAttributeName || expectedAttributeValue + 'set a simple attribute' | 'myUriLdnFirstPart#/attributes/simpleAttribute' || 'simpleAttribute' || '123' + 'set a simple attribute with a trailing /' | 'myUriLdnFirstPart#/attributes/simpleAttribute/' || 'simpleAttribute' || '123' + 'set a complex attribute' | 'myUriLdnFirstPart#/attributes/complexAttribute/simpleAttribute' || 'complexAttribute' || '[simpleAttribute:123]' } def 'Build policy executor patch operation details from ProvMnS request parameters with invalid op.'() { -- 2.16.6