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;
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;
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);
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);
}
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<String, List<ClassInstance>> 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,
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";
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
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'
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
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)
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)'
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)
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'
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'
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
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<String, List<OperationEntry>> changeRequest) {}
+public record OperationDetails(OperationType operationType,
+ String parentFdn,
+ String className,
+ List<ClassInstance> ClassInstances) {}
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;
@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 = "(^/)|(/$)";
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,
final Object resourceAsObject) {
final ResourceObjectDetails resourceObjectDetails = createResourceObjectDetails(resourceAsObject,
requestParameters);
- final OperationEntry operationEntry = new OperationEntry(resourceObjectDetails.id(),
- resourceObjectDetails.attributes());
- final Map<String, List<OperationEntry>> 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<ClassInstance> 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<String, Object> 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<String, Object> attributeHierarchyAsMap = createNestedMap(patchItem);
- final OperationEntry operationEntry = new OperationEntry(requestParameters.id(), attributeHierarchyAsMap);
- final String targetIdentifier = ParameterHelper.extractParentFdn(requestParameters.fdn());
- final Map<String, List<OperationEntry>> 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<ClassInstance> classInstances
+ = List.of(new ClassInstance(requestParameters.id(), attributeHierarchyAsMap));
+ return new OperationDetails(UPDATE, parentFdn, requestParameters.className(), classInstances);
}
@SuppressWarnings("unchecked")
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'
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
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.'() {
def logAppender = Spy(ListAppender<ILoggingEvent>)
- 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()
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).'() {
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")
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'))
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'