2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2021 Bell Canada
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.cps.ncmp.api.impl
25 import com.fasterxml.jackson.core.JsonProcessingException
26 import com.fasterxml.jackson.databind.ObjectMapper
27 import org.onap.cps.api.CpsDataService
28 import org.onap.cps.api.CpsQueryService
29 import org.onap.cps.ncmp.api.impl.exception.NcmpException
30 import org.onap.cps.ncmp.api.impl.operation.DmiOperations
31 import org.onap.cps.ncmp.api.models.CmHandle
32 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
33 import org.onap.cps.spi.FetchDescendantsOption
34 import org.onap.cps.spi.model.DataNode
35 import org.springframework.http.HttpStatus
36 import org.springframework.http.ResponseEntity
37 import spock.lang.Shared
38 import spock.lang.Specification
40 class NetworkCmProxyDataServiceImplSpec extends Specification {
43 def persistenceCmHandle = new CmHandle()
45 def cmHandlesArray = ['cmHandle001']
47 def mockCpsDataService = Mock(CpsDataService)
48 def mockCpsQueryService = Mock(CpsQueryService)
49 def mockDmiOperations = Mock(DmiOperations)
50 def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsDataService, mockCpsQueryService, new ObjectMapper())
52 def cmHandle = 'some handle'
53 def noTimestamp = null
54 def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
56 def expectedDataspaceName = 'NFP-Operational'
57 def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
58 given: 'a cm Handle and a cps path'
59 def cpsPath = '/cps-path'
60 when: 'queryDataNodes is invoked'
61 objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
62 then: 'the persistence service is called once with the correct parameters'
63 1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
64 where: 'all fetch descendants options are supported'
65 fetchDescendantsOption << FetchDescendantsOption.values()
67 def 'Create full data node: #scenario.'() {
68 given: 'a cm handle and root xpath'
69 def jsonData = 'some json'
70 when: 'createDataNode is invoked'
71 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
72 then: 'the CPS service method is invoked once with the expected parameters'
73 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData, noTimestamp)
74 where: 'following parameters were used'
77 'root level xpath' | '/'
79 def 'Create child data node.'() {
80 given: 'a cm handle and parent node xpath'
81 def jsonData = 'some json'
82 def xpath = '/test-node'
83 when: 'createDataNode is invoked'
84 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
85 then: 'the CPS service method is invoked once with the expected parameters'
86 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
88 def 'Add list-node elements.'() {
89 given: 'a cm handle and parent node xpath'
90 def jsonData = 'some json'
91 def xpath = '/test-node'
92 when: 'addListNodeElements is invoked'
93 objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
94 then: 'the CPS service method is invoked once with the expected parameters'
95 1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
97 def 'Update data node leaves.'() {
98 given: 'a cm Handle and a cps path'
100 def jsonData = 'some json'
101 when: 'updateNodeLeaves is invoked'
102 objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
103 then: 'the persistence service is called once with the correct parameters'
104 1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
106 def 'Replace data node tree.'() {
107 given: 'a cm Handle and a cps path'
109 def jsonData = 'some json'
110 when: 'replaceNodeTree is invoked'
111 objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
112 then: 'the persistence service is called once with the correct parameters'
113 1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
116 def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
117 given: 'a registration '
118 def dmiPluginRegistration = new DmiPluginRegistration()
119 dmiPluginRegistration.dmiPlugin = 'my-server'
120 persistenceCmHandle.cmHandleID = '123'
121 persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
122 dmiPluginRegistration.createdCmHandles = createdCmHandles
123 dmiPluginRegistration.updatedCmHandles = updatedCmHandles
124 dmiPluginRegistration.removedCmHandles = removedCmHandles
125 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
126 when: 'registration is updated'
127 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
128 then: 'the CPS save list node data is invoked with the expected parameters'
129 expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
130 '/dmi-registry', expectedJsonData, noTimestamp)
131 and: 'update Node and Child Data Nodes is invoked with correct parameters'
132 expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin',
133 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp)
134 and : 'delete list data node is invoked with the correct parameters'
135 expectedCallsToDeleteListDataNode * mockCpsDataService.deleteListNodeData('NCMP-Admin',
136 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
139 scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
140 'create' | [persistenceCmHandle ] | [] | [] || 1 | 0 | 0
141 'update' | [] | [persistenceCmHandle ] | [] || 0 | 1 | 0
142 'delete' | [] | [] | cmHandlesArray || 0 | 0 | 1
143 'create, update and delete' | [persistenceCmHandle ] | [persistenceCmHandle ] | cmHandlesArray || 1 | 1 | 1
147 def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
148 given: 'a registration without cmHandle properties '
149 def dmiPluginRegistration = new DmiPluginRegistration()
150 dmiPluginRegistration.dmiPlugin = 'my-server'
151 persistenceCmHandle.cmHandleID = '123'
152 persistenceCmHandle.cmHandleProperties = null
153 dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
154 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
155 when: 'registration is updated'
156 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
157 then: 'the CPS save list node data is invoked with the expected parameters'
158 1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
159 '/dmi-registry', expectedJsonData, noTimestamp)
162 def 'Get resource data for pass-through operational from dmi.'() {
163 given: 'data node representing cmHandle and its properties'
164 def cmHandleDataNode = prepareCmHandleDataNode()
165 when: 'get resource data is called'
166 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
171 then: 'cps data service is being called once to get data node'
172 1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
173 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
174 and: 'dmi operation is being called to get resource data'
175 1 * mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
181 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >>
182 new ResponseEntity<>('result-json', HttpStatus.OK)
183 and: 'dmi returns ok response'
184 response == 'result-json'
187 def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
188 given: 'data node representing cmHandle and its properties'
189 def cmHandleDataNode = prepareCmHandleDataNode()
190 and: 'cps data service returns valid cmHandle data node'
191 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
192 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
193 and: 'objectMapper not able to parse object'
194 def mockObjectMapper = Mock(ObjectMapper)
195 objectUnderTest.objectMapper = mockObjectMapper
196 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
197 when: 'get resource data is called'
198 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
203 then: 'exception is thrown'
204 thrown(NcmpException.class)
207 def 'Get resource data for pass-through operational from dmi return NOK response.'() {
208 given: 'data node representing cmHandle and its properties'
209 def cmHandleDataNode = prepareCmHandleDataNode()
210 and: 'cps data service returns valid cmHandle data node'
211 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
212 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
213 and: 'dmi returns NOK response'
214 mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
220 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
221 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
222 when: 'get resource data is called'
223 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
228 then: 'exception is thrown'
229 thrown(NcmpException.class)
232 def 'Get resource data for pass-through running from dmi.'() {
233 given: 'data node representing cmHandle and its properties'
234 def cmHandleDataNode = prepareCmHandleDataNode()
235 and: 'cpsDataService returns valid dataNode'
236 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
237 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
238 and: 'dmi returns valid response and data'
239 mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
245 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
246 when: 'get resource data is called'
247 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
252 then: 'get resource data returns expected response'
253 response == '{result-json}'
256 def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
257 given: 'data node representing cmHandle and its properties'
258 def cmHandleDataNode = prepareCmHandleDataNode()
259 and: 'cpsDataService returns valid dataNode'
260 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
261 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
262 and: 'objectMapper not able to parse object'
263 def mockObjectMapper = Mock(ObjectMapper)
264 objectUnderTest.objectMapper = mockObjectMapper
265 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
266 when: 'get resource data is called'
267 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
272 then: 'exception is thrown'
273 thrown(NcmpException.class)
276 def 'Get resource data for pass-through running from dmi return NOK response.'() {
277 given: 'data node representing cmHandle and its properties'
278 def cmHandleDataNode = prepareCmHandleDataNode()
279 and: 'cpsDataService returns valid dataNode'
280 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
281 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
282 and: 'dmi returns NOK response'
283 mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
289 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
290 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
291 when: 'get resource data is called'
292 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
297 then: 'exception is thrown'
298 thrown(NcmpException.class)
301 def 'Write resource data for pass-through running from dmi using POST.'() {
302 given: 'data node representing cmHandle and its properties'
303 def cmHandleDataNode = prepareCmHandleDataNode()
304 and: 'cpsDataService returns valid dataNode'
305 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
306 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
307 when: 'get resource data is called'
308 objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
310 '{some-json}', 'application/json')
311 then: 'dmi called with correct data'
312 1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi('testDmiService',
315 '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":{"testName":"testValue"}}')
316 >> { new ResponseEntity<>(HttpStatus.CREATED) }
319 def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
320 given: 'data node representing cmHandle and its properties'
321 def cmHandleDataNode = prepareCmHandleDataNode()
322 and: 'cpsDataService returns valid dataNode'
323 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
324 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
325 and: 'dmi throws exception'
326 1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String)
327 >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
328 when: 'get resource data is called'
329 objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
331 '{some-json}', 'application/json')
332 then: 'exception is thrown'
333 thrown(NcmpException.class)
336 private DataNode prepareCmHandleDataNode() {
337 def cmHandleDataNode = new DataNode()
338 cmHandleDataNode.leaves = ['dmi-service-name': 'testDmiService']
339 def cmHandlePropertyDataNode = new DataNode()
340 cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
341 cmHandleDataNode.childDataNodes = [cmHandlePropertyDataNode]