From ffa159fc11f754c96702855b2c84efbf538bca14 Mon Sep 17 00:00:00 2001 From: seanbeirne Date: Tue, 13 Jan 2026 11:37:30 +0000 Subject: [PATCH] ProvMnS correct parameters send to PolicyExecutor(class) - Improved Policy Executor unit test to include request body check - remove AlternateId prefix from ResourceIdentifier - remove wrapping of changeRequest Issue-ID: CPS-3119 Change-Id: I4e0e159e180b09a82c9702035bdef625750f8521 Signed-off-by: seanbeirne --- .../ncmp/rest/controller/ProvMnSController.java | 25 +++++++----- .../rest/controller/ProvMnSControllerSpec.groovy | 43 ++++++++++---------- .../{OperationEntry.java => ClassInstance.java} | 2 +- .../impl/data/policyexecutor/OperationDetails.java | 9 +++-- .../policyexecutor/OperationDetailsFactory.java | 47 +++++++++------------- .../OperationDetailsFactorySpec.groovy | 30 +++++--------- .../data/policyexecutor/PolicyExecutorSpec.groovy | 21 ++++++++-- .../ncmp/provmns/ProvMnSRestApiSpec.groovy | 8 ++-- 8 files changed, 92 insertions(+), 93 deletions(-) rename cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/{OperationEntry.java => ClassInstance.java} (94%) diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnSController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnSController.java index 881dfff82c..7de3891704 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnSController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnSController.java @@ -21,7 +21,6 @@ package org.onap.cps.ncmp.rest.controller; import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE; -import static org.onap.cps.ncmp.impl.data.policyexecutor.OperationDetailsFactory.DELETE_OPERATION_DETAILS; import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA; import static org.onap.cps.ncmp.impl.provmns.ParameterHelper.NO_OP; import static org.springframework.http.HttpStatus.BAD_REQUEST; @@ -35,15 +34,17 @@ import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; import io.netty.handler.timeout.TimeoutException; import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.exceptions.DataValidationException; -import org.onap.cps.ncmp.api.data.models.OperationType; import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException; import org.onap.cps.ncmp.api.exceptions.ProvMnSException; import org.onap.cps.ncmp.api.inventory.models.CmHandleState; import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException; +import org.onap.cps.ncmp.impl.data.policyexecutor.ClassInstance; import org.onap.cps.ncmp.impl.data.policyexecutor.OperationDetails; import org.onap.cps.ncmp.impl.data.policyexecutor.OperationDetailsFactory; import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor; @@ -134,7 +135,7 @@ public class ProvMnSController implements ProvMnS { final YangModelCmHandle yangModelCmHandle = getAndValidateYangModelCmHandle(requestParameters); final OperationDetails operationDetails = operationDetailsFactory.buildOperationDetails(CREATE, requestParameters, resource); - checkPermission(yangModelCmHandle, requestParameters.fdn(), operationDetails); + checkPermission(yangModelCmHandle, operationDetails); final UrlTemplateParameters urlTemplateParameters = parametersBuilder.createUrlTemplateParametersForWrite(yangModelCmHandle, requestParameters.fdn()); return dmiRestClient.synchronousPutOperation(DATA, resource, urlTemplateParameters); @@ -148,7 +149,9 @@ public class ProvMnSController implements ProvMnS { final RequestParameters requestParameters = ParameterHelper.extractRequestParameters(httpServletRequest); try { final YangModelCmHandle yangModelCmHandle = getAndValidateYangModelCmHandle(requestParameters); - checkPermission(yangModelCmHandle, requestParameters.fdn(), DELETE_OPERATION_DETAILS); + final OperationDetails operationDetails = + operationDetailsFactory.buildOperationDetailsForDelete(requestParameters.fdn()); + checkPermission(yangModelCmHandle, operationDetails); final UrlTemplateParameters urlTemplateParameters = parametersBuilder.createUrlTemplateParametersForWrite(yangModelCmHandle, requestParameters.fdn()); return dmiRestClient.synchronousDeleteOperation(DATA, urlTemplateParameters); @@ -179,12 +182,14 @@ public class ProvMnSController implements ProvMnS { } private void checkPermission(final YangModelCmHandle yangModelCmHandle, - final String resourceIdentifier, final OperationDetails operationDetails) { - final OperationType operationType = OperationType.fromOperationName(operationDetails.operation()); - final String operationDetailsAsJson = jsonObjectMapper.asJsonString(operationDetails); - policyExecutor.checkPermission(yangModelCmHandle, operationType, NO_AUTHORIZATION, resourceIdentifier, - operationDetailsAsJson); + final Map> changeRequestAsMap = new HashMap<>(1); + changeRequestAsMap.put(operationDetails.className(), operationDetails.ClassInstances()); + final String changeRequestAsJson = jsonObjectMapper.asJsonString(changeRequestAsMap); + final int index = yangModelCmHandle.getAlternateId().length(); + final String resourceIdentifier = operationDetails.parentFdn().substring(index); + policyExecutor.checkPermission(yangModelCmHandle, operationDetails.operationType(), + NO_AUTHORIZATION, resourceIdentifier, changeRequestAsJson); } private void checkPermissionForEachPatchItem(final String baseFdn, @@ -197,7 +202,7 @@ public class ProvMnSController implements ProvMnS { final OperationDetails operationDetails = operationDetailsFactory.buildOperationDetails(requestParameters, patchItem); try { - checkPermission(yangModelCmHandle, requestParameters.fdn(), operationDetails); + checkPermission(yangModelCmHandle, operationDetails); patchItemCounter++; } catch (final Exception exception) { final String httpMethodName = "PATCH"; diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnSControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnSControllerSpec.groovy index f2edc9c1ed..f4d2bcf5a5 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnSControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnSControllerSpec.groovy @@ -94,17 +94,16 @@ class ProvMnSControllerSpec extends Specification { JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(objectMapper)) static def resourceAsJson = '{"id":"test", "objectClass": "Test", "attributes": { "attr1": "value1"} }' - static def validCmHandle = new YangModelCmHandle(id:'ch-1', dmiServiceName: 'someDmiService', dataProducerIdentifier: 'someDataProducerId', compositeState: new CompositeState(cmHandleState: READY)) + static def validCmHandle = new YangModelCmHandle(id:'ch-1', alternateId: '/managedElement=1', dmiServiceName: 'someDmiService', dataProducerIdentifier: 'someDataProducerId', compositeState: new CompositeState(cmHandleState: READY)) static def cmHandleWithoutDataProducer = new YangModelCmHandle(id:'ch-1', dmiServiceName: 'someDmiService', compositeState: new CompositeState(cmHandleState: READY)) static def cmHandleNotReady = new YangModelCmHandle(id:'ch-1', dmiServiceName: 'someDmiService', dataProducerIdentifier: 'someDataProducerId', compositeState: new CompositeState(cmHandleState: ADVISED)) static def patchMediaType = new MediaType('application', 'json-patch+json') static def patchMediaType3gpp = new MediaType('application', '3gpp-json-patch+json') - static def patchJsonBody = '[{"op":"replace","path":"/child=id2/attributes","value":{"attr1":"test"}}]' + static def patchJsonBody = '[{"op":"replace","path":"/child=id2/attributes","value":{"id":"id1","attributes":{"attr1":"test"}}}]' static def patchJsonBody3gpp = '[{"op":"replace","path":"/child=id2#/attributes/attr1","value":"test"}]' - static def patchJsonBodyInvalid = '[{"op":"replace","path":"/test","value":{}}]' - static def expectedDeleteOperationDetails = '{"operation":"delete","targetIdentifier":"","changeRequest":{}}' + static def expectedDeleteChangeRequest = '{"":[]}' @Value('${rest.api.provmns-base-path}') def provMnSBasePath @@ -222,13 +221,13 @@ class ProvMnSControllerSpec extends Specification { def 'Patch request with #scenario.'() { given: 'provmns url' - def provmnsUrl = "$provMnSBasePath/v1/myClass=id1" + def provmnsUrl = "$provMnSBasePath/v1/managedElement=1/myClass=id1" and: 'alternate Id can be matched' - mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/myClass=id1', "/") >> 'ch-1' + mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/managedElement=1/myClass=id1', "/") >> 'ch-1' and: 'resource id for policy executor points to child node' - def expectedResourceIdForPolicyExecutor = '/myClass=id1/child=id2' + def expectedResourceIdForPolicyExecutor = '/myClass=id1' and: 'operation details has correct class and attributes, target identifier points to parent' - def expectedOperationDetails = '{"operation":"update","targetIdentifier":"/myClass=id1","changeRequest":{"child":[{"id":"id2","attributes":{"attr1":"test"}}]}}' + def expectedChangeRequest = '{"child":[{"id":"id2","attributes":{"attr1":"test"}}]}' and: 'persistence service returns yangModelCmHandle' mockInventoryPersistence.getYangModelCmHandle('ch-1') >> validCmHandle and: 'dmi provides a response' @@ -243,7 +242,7 @@ class ProvMnSControllerSpec extends Specification { and: 'the response contains the expected content' assert response.contentAsString.contains('content from DMI') and: 'policy executor was invoked with the expected parameters' - 1 * mockPolicyExecutor.checkPermission(_, OperationType.UPDATE, _, expectedResourceIdForPolicyExecutor, expectedOperationDetails) + 1 * mockPolicyExecutor.checkPermission(validCmHandle, OperationType.UPDATE, _, expectedResourceIdForPolicyExecutor, expectedChangeRequest) where: 'following scenarios are applied' scenario | contentMediaType | jsonBody | responseStatusFromDmi || expectedResponseStatusFromProvMnS 'happy flow 3gpp' | patchMediaType3gpp | patchJsonBody3gpp | OK || OK @@ -292,12 +291,12 @@ class ProvMnSControllerSpec extends Specification { def 'Patch remove request.'() { given: 'resource data url' - def url = "$provMnSBasePath/v1/myClass=id1" + def url = "$provMnSBasePath/v1/managedElement=1/myClass=id1" and: 'alternate Id can be matched' - mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/myClass=id1', "/") >> 'ch-1' + mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/managedElement=1/myClass=id1', "/") >> 'ch-1' and: 'persistence service returns valid yangModelCmHandle' mockInventoryPersistence.getYangModelCmHandle('ch-1') >> validCmHandle - def expectedResourceIdentifier = '/myClass=id1/childClass=1/grandchildClass=1' + def expectedResourceIdentifier = '/myClass=id1/childClass=1' when: 'patch data resource request is performed' def response = mvc.perform(patch(url) .contentType(patchMediaType) @@ -306,14 +305,14 @@ class ProvMnSControllerSpec extends Specification { then: 'response status is OK' assert response.status == OK.value() and: 'Policy Executor was invoked with correct details' - 1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, _, expectedResourceIdentifier, expectedDeleteOperationDetails) + 1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, _, expectedResourceIdentifier, expectedDeleteChangeRequest) } def 'Patch request with no permission from Coordination Management (aka Policy Executor).'() { given: 'resource data url' - def url = "$provMnSBasePath/v1/myClass=id1" + def url = "$provMnSBasePath/v1/ManageElement=1/myClass=id1" and: 'alternate Id can be matched' - mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/myClass=id1', "/") >> 'ch-1' + mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/ManageElement=1/myClass=id1', "/") >> 'ch-1' and: 'persistence service returns valid yangModelCmHandle' mockInventoryPersistence.getYangModelCmHandle('ch-1') >> validCmHandle and: 'the permission is denied (Policy Executor throws an exception)' @@ -374,17 +373,17 @@ class ProvMnSControllerSpec extends Specification { def 'Put resource data request with #scenario.'() { given: 'resource data url' - def putUrl = "$provMnSBasePath/v1/myClass=id1/childClass=1/grandChildClass=2" + def putUrl = "$provMnSBasePath/v1/ManagedElement=1/myClass=id1/childClass=1/grandChildClass=2" and: 'alternate Id can be matched' - mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/myClass=id1/childClass=1/grandChildClass=2', "/") >> 'ch-1' + mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/ManagedElement=1/myClass=id1/childClass=1/grandChildClass=2', "/") >> 'ch-1' and: 'persistence service returns yangModelCmHandle' mockInventoryPersistence.getYangModelCmHandle('ch-1') >> validCmHandle and: 'dmi provides a response' mockDmiRestClient.synchronousPutOperation(*_) >> new ResponseEntity<>(responseContentFromDmi, responseStatusFromDmi) and: 'The expected resource identifier for policy executor is the FDN to grandchild' - def expectedResourceIdentifier = '/myClass=id1/childClass=1/grandChildClass=2' + def expectedResourceIdentifier = '/myClass=id1/childClass=1' and: 'The change request target identifier is the FDN to parent and last class as object name in change request' - def expectedChangeRequest = '{"operation":"create","targetIdentifier":"/myClass=id1/childClass=1","changeRequest":{"grandChildClass":[{"id":"2","attributes":{"attr1":"value1"}}]}}' + def expectedChangeRequest = '{"grandChildClass":[{"id":"2","attributes":{"attr1":"value1"}}]}' when: 'put data resource request is performed' def response = mvc.perform(put(putUrl) .contentType(MediaType.APPLICATION_JSON) @@ -420,9 +419,9 @@ class ProvMnSControllerSpec extends Specification { def 'Delete resource data request with #scenario.'() { given: 'resource data url' - def deleteUrl = "$provMnSBasePath/v1/myClass=id1/childClass=1/grandChildClass=2" + def deleteUrl = "$provMnSBasePath/v1/ManagedElement=1/myClass=id1/childClass=1/grandChildClass=2" and: 'alternate Id can be matched' - mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/myClass=id1/childClass=1/grandChildClass=2', "/") >> 'ch-1' + mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId('/ManagedElement=1/myClass=id1/childClass=1/grandChildClass=2', "/") >> 'ch-1' and: 'persistence service returns yangModelCmHandle' mockInventoryPersistence.getYangModelCmHandle('ch-1') >> validCmHandle and: 'dmi provides a response' @@ -434,7 +433,7 @@ class ProvMnSControllerSpec extends Specification { and: 'the content is whatever the DMI returned' assert response.contentAsString == responseContentFromDmi and: 'Policy Executor was invoked with correct resource identifier and almost empty operation details (not used for delete!)' - 1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, _, '/myClass=id1/childClass=1/grandChildClass=2', expectedDeleteOperationDetails) + 1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, _, '/myClass=id1/childClass=1', expectedDeleteChangeRequest) where: 'following responses returned by DMI' scenario | responseStatusFromDmi | responseContentFromDmi 'happy flow' | OK | 'content from DMI' diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationEntry.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/ClassInstance.java similarity index 94% rename from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationEntry.java rename to cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/ClassInstance.java index 184234f5d8..c86ab30a56 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationEntry.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/ClassInstance.java @@ -20,4 +20,4 @@ package org.onap.cps.ncmp.impl.data.policyexecutor; -public record OperationEntry(String id, Object attributes) {} \ No newline at end of file +public record ClassInstance(String id, Object attributes) {} \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetails.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetails.java index 7c64283e2a..23dfff1c52 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetails.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetails.java @@ -22,9 +22,10 @@ package org.onap.cps.ncmp.impl.data.policyexecutor; import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; -import java.util.Map; +import org.onap.cps.ncmp.api.data.models.OperationType; @JsonInclude(JsonInclude.Include.NON_NULL) -public record OperationDetails(String operation, - String targetIdentifier, - Map> changeRequest) {} +public record OperationDetails(OperationType operationType, + String parentFdn, + String className, + List ClassInstances) {} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactory.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactory.java index 2ad53cbe7d..5d5ef96a84 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactory.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactory.java @@ -20,7 +20,7 @@ package org.onap.cps.ncmp.impl.data.policyexecutor; -import static java.util.Collections.emptyMap; +import static java.util.Collections.emptyList; import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE; import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE; @@ -43,8 +43,6 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class OperationDetailsFactory { - public static final OperationDetails DELETE_OPERATION_DETAILS = new OperationDetails("delete", "", emptyMap()); - private static final String ATTRIBUTE_NAME_SEPARATOR = "/"; private static final String REGEX_FOR_LEADING_AND_TRAILING_SEPARATORS = "(^/)|(/$)"; @@ -62,17 +60,17 @@ public class OperationDetailsFactory { final OperationDetails operationDetails; switch (patchItem.getOp()) { case ADD: - operationDetails = buildOperationDetailsForPatchItem(CREATE, requestParameters, patchItem); + operationDetails = buildOperationDetails(CREATE, requestParameters, patchItem.getValue()); break; case REPLACE: if (patchItem.getPath().contains("#/attributes")) { operationDetails = buildOperationDetailsForPatchItemWithHash(requestParameters, patchItem); } else { - operationDetails = buildOperationDetailsForPatchItem(UPDATE, requestParameters, patchItem); + operationDetails = buildOperationDetails(UPDATE, requestParameters, patchItem.getValue()); } break; case REMOVE: - operationDetails = DELETE_OPERATION_DETAILS; + operationDetails = buildOperationDetailsForDelete(requestParameters.fdn()); break; default: throw new ProvMnSException("PATCH", HttpStatus.UNPROCESSABLE_ENTITY, @@ -94,39 +92,30 @@ public class OperationDetailsFactory { final Object resourceAsObject) { final ResourceObjectDetails resourceObjectDetails = createResourceObjectDetails(resourceAsObject, requestParameters); - final OperationEntry operationEntry = new OperationEntry(resourceObjectDetails.id(), - resourceObjectDetails.attributes()); - final Map> changeRequestAsMap = - Map.of(resourceObjectDetails.objectClass(), List.of(operationEntry)); - final String targetIdentifier = ParameterHelper.extractParentFdn(requestParameters.fdn()); - return new OperationDetails(operationType.getOperationName(), targetIdentifier, changeRequestAsMap); + final String parentFdn = ParameterHelper.extractParentFdn(requestParameters.fdn()); + final List classInstances + = List.of(new ClassInstance(resourceObjectDetails.id(), resourceObjectDetails.attributes())); + return new OperationDetails(operationType, parentFdn, resourceObjectDetails.objectClass(), classInstances); } /** - * Build OperationDetails for a specific patch item. + * Build a OperationDetails object from ProvMnS request details for delete. * - * @param operationType the type of operation (CREATE, UPDATE) - * @param requestParameters request parameters including uri-ldn-first-part, className and id - * @param patchItem the patch item containing operation details - * @return OperationDetails object for the patch item + * @param fdn fdn to be deleted + * @return OperationDetails object */ - public OperationDetails buildOperationDetailsForPatchItem(final OperationType operationType, - final RequestParameters requestParameters, - final PatchItem patchItem) { - final Map resourceAsObject = new HashMap<>(2); - resourceAsObject.put("id", requestParameters.id()); - resourceAsObject.put("attributes", patchItem.getValue()); - return buildOperationDetails(operationType, requestParameters, resourceAsObject); + public OperationDetails buildOperationDetailsForDelete(final String fdn) { + final String parentFdn = ParameterHelper.extractParentFdn(fdn); + return new OperationDetails(OperationType.DELETE, parentFdn, "", emptyList()); } private OperationDetails buildOperationDetailsForPatchItemWithHash(final RequestParameters requestParameters, final PatchItem patchItem) { final Map attributeHierarchyAsMap = createNestedMap(patchItem); - final OperationEntry operationEntry = new OperationEntry(requestParameters.id(), attributeHierarchyAsMap); - final String targetIdentifier = ParameterHelper.extractParentFdn(requestParameters.fdn()); - final Map> operationEntriesPerObjectClass = new HashMap<>(); - operationEntriesPerObjectClass.put(requestParameters.className(), List.of(operationEntry)); - return new OperationDetails(UPDATE.getOperationName(), targetIdentifier, operationEntriesPerObjectClass); + final String parentFdn = ParameterHelper.extractParentFdn(requestParameters.fdn()); + final List classInstances + = List.of(new ClassInstance(requestParameters.id(), attributeHierarchyAsMap)); + return new OperationDetails(UPDATE, parentFdn, requestParameters.className(), classInstances); } @SuppressWarnings("unchecked") diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactorySpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactorySpec.groovy index 1c41b4293c..f9a9894990 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactorySpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactorySpec.groovy @@ -35,29 +35,19 @@ import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE class OperationDetailsFactorySpec extends Specification { def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - def requestPathParameters = new RequestParameters('some method', '/parent=1/child=2','some uri', 'class in uri', 'id in uri') + def requestPathParameters = new RequestParameters('some method', '/parent=id1/someChild=someId','some uri', 'class from uri', 'id from uri') OperationDetailsFactory objectUnderTest = new OperationDetailsFactory(jsonObjectMapper) - def 'Build create operation details with all properties.'() { - given: 'a resource' - def resource = new ResourceOneOf(id: 'id in resource', objectClass: 'class in resource') - when: 'create operation details are built' - def result = objectUnderTest.buildOperationDetails(CREATE, requestPathParameters, resource) - then: 'all details are correct' - assert result.targetIdentifier == '/parent=1' - assert result.changeRequest.keySet()[0] == 'class in uri' - assert result.changeRequest['class in uri'][0].id == 'id in uri' - } - def 'Build replace (~create) operation details with all properties where class name in body is #scenario.'() { given: 'a resource' def resource = new ResourceOneOf(id: 'some resource id', objectClass: classNameInBody) when: 'replace operation details are built' def result = objectUnderTest.buildOperationDetails(CREATE, requestPathParameters, resource) then: 'all details are correct' - assert result.targetIdentifier == '/parent=1' - assert result.changeRequest.keySet()[0] == 'class in uri' + assert result.parentFdn == '/parent=id1' + assert result.className == 'class from uri' + assert result.ClassInstances[0].id == 'id from uri' where: scenario | classNameInBody 'populated' | 'class in body' @@ -72,7 +62,7 @@ class OperationDetailsFactorySpec extends Specification { when: 'operation details is created' def result = objectUnderTest.buildOperationDetails(requestPathParameters, patchItem) then: 'it has the correct operation type (for Policy Executor check)' - assert result.operation() == expectedPolicyExecutorOperationType.operationName + assert result.operationType == expectedPolicyExecutorOperationType where: 'following operations are used' patchOperationType | expectedPolicyExecutorOperationType 'ADD' | CREATE @@ -85,12 +75,12 @@ class OperationDetailsFactorySpec extends Specification { def patchItem = new PatchItem(op: 'REPLACE', 'path':"some uri${suffix}", value: value) when: 'patch operation details are checked' def result = objectUnderTest.buildOperationDetails(requestPathParameters, patchItem) - then: 'Attribute Value in operation is correct' - result.changeRequest.values()[0].attributes[0] == expectedAttributesValueInOperation + then: 'Attribute value is correct' + result.ClassInstances[0].attributes == [attr1:456] where: 'attributes are set using # or resource' - scenario | suffix | value || expectedAttributesValueInOperation - 'set simple value using #' | '#/attributes/attr1' | 1 || [attr1:1] - 'set complex value using resource' | '' | '{"attr1":"abc","attr2":123}' || '{"attr1":"abc","attr2":123}' + scenario | suffix | value + 'set simple value using #' | '#/attributes/attr1' | 456 + 'set complex value using resource' | '' | [id:'id1', attributes:[attr1:456]] } def 'Build an attribute map with different depths of hierarchy with #scenario.'() { 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 96330ab8ec..c6312020cc 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 @@ -55,7 +55,11 @@ class PolicyExecutorSpec extends Specification { def logAppender = Spy(ListAppender) - def someValidJson = '{"Hello":"World"}' + def static someValidJson = '{"Hello":"World"}' + def static provMnSExampleJson = '{"grandChildClass":[{"id":"2","attributes":{"attr1":"value1"}}]}' + def static grandChildInstance = [id:'2', attributes:[attr1:'value1']] + def static provMnSExampleAsMap = [grandChildClass: [grandChildInstance]] + def static NO_CHANGE_REQUEST_FOR_DELETE = null def setup() { setupLogger() @@ -77,13 +81,24 @@ class PolicyExecutorSpec extends Specification { given: 'allow response' mockResponse([permissionResult:'allow'], HttpStatus.OK) when: 'permission is checked for an operation' - objectUnderTest.checkPermission(new YangModelCmHandle(), operationType, 'my credentials','my resource',someValidJson) + objectUnderTest.checkPermission(new YangModelCmHandle(), operationType, 'my credentials','my resource',provMnSExampleJson) then: 'system logs the operation is allowed' assert getLogEntry(4) == 'Operation allowed.' + and: 'the request body sent has expected operation and change request' + 1 * mockRequestBodyUriSpec.body(*_) >> { args -> + def firstOperationAsMap = args[0].arg$1.get('operations')[0] + assert firstOperationAsMap.get('operation') == operationType.operationName + assert firstOperationAsMap.get('changeRequest') == expectedChangeRequest + return mockRequestBodyUriSpec + } and: 'no exception occurs' noExceptionThrown() where: 'all write operations are tested' - operationType << [ CREATE, DELETE, PATCH, UPDATE ] + operationType || expectedChangeRequest + CREATE || provMnSExampleAsMap + DELETE || NO_CHANGE_REQUEST_FOR_DELETE + PATCH || provMnSExampleAsMap + UPDATE || provMnSExampleAsMap } def 'Permission check with "other" decision (not allowed).'() { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/provmns/ProvMnSRestApiSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/provmns/ProvMnSRestApiSpec.groovy index 78ae3d03cb..a46e84ae0e 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/provmns/ProvMnSRestApiSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/provmns/ProvMnSRestApiSpec.groovy @@ -47,7 +47,7 @@ class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{ def 'Put Resource Data from provmns interface.'() { given: 'an example resource json body' dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] - registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, '/A=1/B=2/C=3') + registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, '/A=1/B=2') def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test')) expect: 'an OK response on PUT endpoint' mvc.perform(put("/ProvMnS/v1/A=1/B=2/C=3") @@ -61,8 +61,8 @@ class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{ def 'Patch Resource Data from provmns interface.'() { given: 'an example resource json body' dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] - registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, '/A=1/B=2/C=3') - def jsonBody = jsonObjectMapper.asJsonString([new PatchItem(op: 'REMOVE', path: 'someUriLdnFirstPart')]) + registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, '/A=1/B=2') + def jsonBody = jsonObjectMapper.asJsonString([new PatchItem(op: 'REMOVE', path: '/D=3/C=4')]) expect: 'an OK response on PATCH endpoint' mvc.perform(patch("/ProvMnS/v1/A=1/B=2/C=3") .contentType(new MediaType('application', 'json-patch+json')) @@ -75,7 +75,7 @@ class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{ def 'Delete Resource Data from provmns interface.'() { given: 'a registered cm handle' dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] - registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, '/A=1/B=2/C=3') + registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, '/A=1/B=2') expect: 'ok response on DELETE endpoint' mvc.perform(delete("/ProvMnS/v1/A=1/B=2/C=3")).andExpect(status().isOk()) cleanup: 'deregister CM handles' -- 2.16.6