2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.ncmp.impl.data.policyexecutor
24 import com.fasterxml.jackson.databind.ObjectMapper
25 import org.onap.cps.ncmp.api.exceptions.ProvMnSException
26 import org.onap.cps.ncmp.impl.provmns.RequestParameters
27 import org.onap.cps.ncmp.impl.provmns.model.PatchItem
28 import org.onap.cps.ncmp.impl.provmns.model.ResourceOneOf
29 import org.onap.cps.utils.JsonObjectMapper
30 import spock.lang.Specification
32 import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE
33 import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE
34 import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE
36 class OperationDetailsFactorySpec extends Specification {
38 def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
40 OperationDetailsFactory objectUnderTest = new OperationDetailsFactory(jsonObjectMapper)
42 static def complexValueAsResource = new ResourceOneOf(id: 'myId', attributes: ['myAttribute1:myValue1', 'myAttribute2:myValue2'], objectClass: 'myClassName')
43 static def simpleValueAsResource = new ResourceOneOf(id: 'myId', attributes: ['simpleAttribute:1'], objectClass: 'myClassName')
45 def 'Build create operation details with all properties.'() {
46 given: 'request parameters and resource'
47 def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'my uri', className: 'class in uri', id: 'my id')
48 def resource = new ResourceOneOf(id: 'some resource id', objectClass: 'class in resource')
49 when: 'create operation details are built'
50 def result = objectUnderTest.buildCreateOperationDetails(CREATE, requestPathParameters, resource)
51 then: 'all details are correct'
52 assert result.targetIdentifier == 'my uri'
53 assert result.changeRequest.keySet()[0] == 'class in resource'
54 assert result.changeRequest['class in resource'][0].id == 'my id'
57 def 'Build replace operation details with all properties where class name in body is #scenario.'() {
58 given: 'request parameters and resource'
59 def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'my uri', className: 'class in uri', id: 'some id')
60 def resource = new ResourceOneOf(id: 'some resource id', objectClass: classNameInBody)
61 when: 'replace operation details are built'
62 def result = objectUnderTest.buildCreateOperationDetails(CREATE, requestPathParameters, resource)
63 then: 'all details are correct'
64 assert result.targetIdentifier == 'my uri'
65 assert result.changeRequest.keySet()[0] == expectedChangeRequestKey
67 scenario | classNameInBody || expectedChangeRequestKey
68 'populated' | 'class in body' || 'class in body'
69 'empty' | '' || 'class in uri'
70 'null' | null || 'class in uri'
73 def 'Build delete operation details with all properties'() {
74 given: 'request parameters'
75 def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'my uri', className: 'classNameInUri', id: 'myId')
76 when: 'delete operation details are built'
77 def result = objectUnderTest.buildDeleteOperationDetails(requestPathParameters.toTargetFdn())
78 then: 'all details are correct'
79 assert result.targetIdentifier == 'my uri/classNameInUri=myId'
82 def 'Single patch operation with #patchOperationType checks correct operation type.'() {
83 given: 'request parameters and single patch item'
84 def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'some class')
85 def resource = new ResourceOneOf(id: 'some resource id')
86 def patchItem = new PatchItem(op: patchOperationType, 'path':'some uri', value: resource)
87 when: 'operation details is created'
88 def result = objectUnderTest.buildOperationDetails(requestPathParameters, patchItem)
89 then: 'it has the correct operation type (for Policy Executor check)'
90 assert result.operation() == expectedPolicyExecutorOperationType.name()
91 where: 'following operations are used'
92 patchOperationType | expectedPolicyExecutorOperationType
98 def 'Build policy executor patch operation details with single replace operation and #scenario.'() {
99 given: 'a requestParameter and a patchItem list'
100 def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'some class')
101 def pathItem = new PatchItem(op: 'REPLACE', 'path':"some uri${suffix}", value: value)
102 when: 'patch operation details are checked'
103 def result = objectUnderTest.buildOperationDetails(requestPathParameters, pathItem)
104 then: 'Attribute Value in operation is correct'
105 result.changeRequest.values()[0].attributes[0] == expectedAttributesValueInOperation
106 where: 'attributes are set using # or resource'
107 scenario | suffix | value || expectedAttributesValueInOperation
108 'set simple value using #' | '#/attributes/simpleAttribute' | 1 || [simpleAttribute:1]
109 'set simple value using resource' | '' | simpleValueAsResource || ['simpleAttribute:1']
110 'set complex value using resource' | '' | complexValueAsResource || ["myAttribute1:myValue1","myAttribute2:myValue2"]
113 def 'Build an attribute map with different depths of hierarchy with #scenario.'() {
114 given: 'a patch item with a path'
115 def patchItem = new PatchItem(op: 'REPLACE', 'path':path, value: 123)
116 when: 'transforming the attributes'
117 def hierarchyMap = objectUnderTest.createNestedMap(patchItem)
118 then: 'the map depth is equal to the expected number of attributes'
119 assert hierarchyMap.get(expectedAttributeName).toString() == expectedAttributeValue
120 where: 'simple and complex attributes are tested'
121 scenario | path || expectedAttributeName || expectedAttributeValue
122 'set a simple attribute' | 'myUriLdnFirstPart#/attributes/simpleAttribute' || 'simpleAttribute' || '123'
123 'set a simple attribute with a trailing /' | 'myUriLdnFirstPart#/attributes/simpleAttribute/' || 'simpleAttribute' || '123'
124 'set a complex attribute' | 'myUriLdnFirstPart#/attributes/complexAttribute/simpleAttribute' || 'complexAttribute' || '[simpleAttribute:123]'
127 def 'Attempt to Build Operation details with unsupported op (MOVE).'() {
128 given: 'a provMnsRequestParameter and a patchItem'
129 def path = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'some class')
130 def patchItem = new PatchItem(op: 'MOVE', 'path':'some uri')
131 when: 'a build is attempted with an unsupported op'
132 objectUnderTest.buildOperationDetails(path, patchItem)
133 then: 'the result is as expected (exception thrown)'
134 def exceptionThrown = thrown(ProvMnSException)
135 assert exceptionThrown.title == 'Unsupported Patch Operation Type: move'
138 def 'Build policy executor create operation details from ProvMnS request parameters where objectClass in resource #scenario.'() {
139 given: 'a provMnsRequestParameter and a resource'
140 def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'class in uri', id:'my id')
141 def resource = new ResourceOneOf(id: 'some resource id', objectClass: objectInResouce)
142 when: 'a configurationManagementOperation is created and converted to JSON'
143 def result = objectUnderTest.buildCreateOperationDetails(CREATE, requestPathParameters, resource)
144 then: 'the result is as expected (using json to compare)'
145 String expectedJsonString = '{"operation":"CREATE","targetIdentifier":"some uri","changeRequest":{"' + changeRequestClassReference + '":[{"id":"my id","attributes":null}]}}'
146 assert jsonObjectMapper.asJsonString(result) == expectedJsonString
148 scenario | objectInResouce || changeRequestClassReference
149 'populated' | 'class in resource' || 'class in resource'
150 'empty' | '' || 'class in uri'
151 'null' | null || 'class in uri'