package org.onap.cps.ncmp.rest.controller;
import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE;
+import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE;
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;
final Map<String, List<ClassInstance>> changeRequestAsMap = new HashMap<>(1);
changeRequestAsMap.put(operationDetails.className(), operationDetails.ClassInstances());
final String changeRequestAsJson = jsonObjectMapper.asJsonString(changeRequestAsMap);
- final String resourceIdentifier;
- if (operationDetails.parentFdn().length() <= yangModelCmHandle.getAlternateId().length()) {
- if (operationDetails.parentFdn().isEmpty()) {
- resourceIdentifier = "/";
- } else {
- resourceIdentifier = operationDetails.parentFdn();
- }
- } else {
- final int index = yangModelCmHandle.getAlternateId().length();
- resourceIdentifier = operationDetails.parentFdn().substring(index);
+ if (targetIsRootMo(yangModelCmHandle.getAlternateId(), operationDetails)) {
+ throw new DataValidationException("Data manipulation operations are not supported on "
+ + requestParameters.fdn(), "");
}
+ final int index = yangModelCmHandle.getAlternateId().length();
+ final String resourceIdentifier = operationDetails.parentFdn().substring(index);
policyExecutor.checkPermission(yangModelCmHandle, operationDetails.operationType(),
requestParameters.authorization(), resourceIdentifier, changeRequestAsJson);
}
+ private static boolean targetIsRootMo(final String alternateId, final OperationDetails operationDetails) {
+ if (DELETE.equals(operationDetails.operationType())) {
+ return operationDetails.parentFdn().length() <= alternateId.length();
+ }
+ return operationDetails.parentFdn().length() < alternateId.length();
+ }
+
private void checkPermissionForEachPatchItem(final String baseFdn,
final List<PatchItem> patchItems,
final YangModelCmHandle yangModelCmHandle,
'modify grandchild' | patchMediaType | '/subnetwork=1/managedElement=2' | '/subnetwork=1/managedElement=2/child=id1' | patchJsonBody || '/child=id1' | '{"otherChild":[{"id":"id2","attributes":{"attr1":"test"}}]}'
'3gpp modify grandchild' | patchMediaType3gpp | '/subnetwork=1/managedElement=2' | '/subnetwork=1/managedElement=2/child=id1' | patchJsonBody3gpp || '/child=id1' | '{"otherChild":[{"id":"id2","attributes":{"attr1":"test"}}]}'
'no subnetwork' | patchMediaType | '/managedElement=2' | '/managedElement=2/child=id1' | patchJsonBody || '/child=id1' | '{"otherChild":[{"id":"id2","attributes":{"attr1":"test"}}]}'
- 'modify first child' | patchMediaType | '/subnetwork=1/managedElement=2' | '/subnetwork=1/managedElement=2' | patchJsonBody || '/subnetwork=1/managedElement=2' | '{"otherChild":[{"id":"id2","attributes":{"attr1":"test"}}]}'
- 'modify alternate id' | patchMediaType | '/subnetwork=1/managedElement=2' | '/subnetwork=1/managedElement=2' | patchWithoutChild || '/subnetwork=1' | '{"managedElement":[{"id":"2","attributes":{"attr2":"test2"}}]}'
- 'modify root MO' | patchMediaType | '/managedElement=2' | '/managedElement=2' | patchWithoutChild || '/' | '{"managedElement":[{"id":"2","attributes":{"attr2":"test2"}}]}'
+ 'modify first child' | patchMediaType | '/subnetwork=1/managedElement=2' | '/subnetwork=1/managedElement=2' | patchJsonBody || '' | '{"otherChild":[{"id":"id2","attributes":{"attr1":"test"}}]}'
+ }
+
+ def 'Attempt Patch request with root MO, #scenario.'() {
+ given: 'provmns url'
+ def provmnsUrl = "$provMnSBasePath/v1$fdn"
+ and: 'alternate Id can be matched'
+ mockedCmHandle.getAlternateId() >> alternateId
+ mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId(fdn, "/") >> 'mock'
+ and: 'persistence service returns yangModelCmHandle'
+ mockInventoryPersistence.getYangModelCmHandle('mock') >> mockedCmHandle
+ when: 'patch request is performed'
+ def response = mvc.perform(patch(provmnsUrl)
+ .header('Authorization', 'my authorization')
+ .contentType(patchMediaType)
+ .content(jsonBody))
+ .andReturn().response
+ then: 'policy executor is never invoked'
+ 0 * mockPolicyExecutor._
+ and: 'the response status is BAD_REQUEST'
+ assert response.status == BAD_REQUEST.value()
+ and: 'the response content contains the required error details'
+ assert response.contentAsString.contains('VALIDATION_ERROR')
+ assert response.contentAsString.contains('not supported')
+ assert response.contentAsString.contains(fdn)
+ where: 'following scenarios are applied'
+ scenario | alternateId | fdn | jsonBody
+ 'modify alternate id' | '/subnetwork=1/managedElement=2' | '/subnetwork=1/managedElement=2' | patchWithoutChild
+ 'modify root MO' | '/managedElement=2' | '/managedElement=2' | patchWithoutChild
}
def 'Patch request with error from DMI.'() {
assert response.contentAsString.contains('"title":"/myClass=id1 not found"')
}
+ def 'Attempt Delete root MO, #scenario.'() {
+ given: 'resource data url'
+ def deleteUrl = "$provMnSBasePath/v1$fdn"
+ and: 'alternate Id is mocked can be matched'
+ mockedCmHandle.getAlternateId() >> alternateId
+ mockAlternateIdMatcher.getCmHandleIdByLongestMatchingAlternateId(fdn, "/") >> 'mock'
+ and: 'persistence service returns yangModelCmHandle'
+ mockInventoryPersistence.getYangModelCmHandle('mock') >> mockedCmHandle
+ when: 'Delete data resource request is attempted'
+ def response = mvc.perform(delete(deleteUrl).header('Authorization', 'my authorization')).andReturn().response
+ then: 'policy executor is never invoked'
+ 0 * mockPolicyExecutor._
+ and: 'the response status is BAD_REQUEST'
+ assert response.status == BAD_REQUEST.value()
+ and: 'the response content contains the required error details'
+ assert response.contentAsString.contains('VALIDATION_ERROR')
+ assert response.contentAsString.contains('not supported')
+ assert response.contentAsString.contains(fdn)
+ where: 'root MOs are targeted'
+ scenario | fdn | alternateId
+ 'with subnetwork' | '/Subnetwork=1/ManagedElement=1' | '/subnetwork=1/managedElement=1'
+ 'no subnetwork' | '/ManagedElement=1' | '/managedElement=1'
+ }
+
}