2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2021-2022 Bell Canada
6 * Modifications Copyright (C) 2023 TechMahindra Ltd.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.cps.ncmp.api.impl
26 import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
27 import org.onap.cps.ncmp.api.models.CmResourceAddress
29 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
30 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
31 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
32 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
33 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
34 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
35 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
36 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
38 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
39 import com.hazelcast.map.IMap
40 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
41 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
42 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
43 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
44 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
45 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
46 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
47 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
48 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
49 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
50 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
51 import org.onap.cps.ncmp.api.models.DataOperationDefinition
52 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
53 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
54 import org.onap.cps.ncmp.api.models.ConditionApiProperties
55 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
56 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
57 import org.onap.cps.ncmp.api.models.DataOperationRequest
58 import org.onap.cps.spi.exceptions.CpsException
59 import org.onap.cps.spi.model.ConditionProperties
61 import java.util.stream.Collectors
62 import org.onap.cps.utils.JsonObjectMapper
63 import com.fasterxml.jackson.databind.ObjectMapper
64 import org.onap.cps.api.CpsDataService
65 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
66 import org.onap.cps.spi.FetchDescendantsOption
67 import org.onap.cps.spi.model.DataNode
68 import org.springframework.http.HttpStatus
69 import org.springframework.http.ResponseEntity
70 import spock.lang.Specification
72 class NetworkCmProxyDataServiceImplSpec extends Specification {
74 def mockCpsDataService = Mock(CpsDataService)
75 def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
76 def mockDmiDataOperations = Mock(DmiDataOperations)
77 def nullNetworkCmProxyDataServicePropertyHandler = null
78 def mockInventoryPersistence = Mock(InventoryPersistence)
79 def mockCmHandleQueries = Mock(CmHandleQueries)
80 def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
81 def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
82 def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
83 def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
84 def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
85 def mockTrustLevelManager = Mock(TrustLevelManager)
86 def mockAlternateIdChecker = Mock(AlternateIdChecker)
89 def NO_REQUEST_ID = null
90 def NO_AUTH_HEADER = null
91 def OPTIONS_PARAM = '(a=1,b=2)'
92 def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
94 def objectUnderTest = new NetworkCmProxyDataServiceImpl(
95 spiedJsonObjectMapper,
96 mockDmiDataOperations,
97 nullNetworkCmProxyDataServicePropertyHandler,
98 mockInventoryPersistence,
100 mockCpsCmHandlerQueryService,
101 mockLcmEventsCmHandleStateHandler,
103 stubModuleSyncStartedOnCmHandles,
104 stubTrustLevelPerDmiPlugin,
105 mockTrustLevelManager,
106 mockAlternateIdChecker)
108 def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
110 def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])]
112 def 'Write resource data for pass-through running from DMI using POST.'() {
113 given: 'cpsDataService returns valid datanode'
115 when: 'write resource data is called'
116 objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
117 'testResourceId', CREATE,
118 '{some-json}', 'application/json', null)
119 then: 'DMI called with correct data'
120 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
121 CREATE, '{some-json}', 'application/json', null)
122 >> { new ResponseEntity<>(HttpStatus.CREATED) }
125 def 'Get resource data for from DMI.'() {
126 given: 'cpsDataService returns valid data node'
128 and: 'some cm resource address'
129 def cmResourceAddress = new CmResourceAddress('some datastore','some CM Handle', 'some resource Id')
130 and: 'get resource data from DMI is called'
131 mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >>
132 new ResponseEntity<>('dmi-response', HttpStatus.OK)
133 when: 'get resource data operational for the given cm resource address is called'
134 def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER)
135 then: 'DMI returns a json response'
136 assert response == 'dmi-response'
139 def 'Get resource data for operational (cached) data.'() {
140 given: 'CPS Data service returns some object(s)'
141 mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
142 and: 'a cm resource address for the same datastore, cm handle and resource id'
143 def cmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId')
144 when: 'get resource data is called'
145 def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, FetchDescendantsOption.OMIT_DESCENDANTS)
146 then: 'get resource data returns teh first object from the data service'
147 assert response == 'First Object'
150 def 'Execute (async) data operation for #datastoreName from DMI.'() {
151 given: 'cpsDataService returns valid data node'
152 def dataOperationRequest = getDataOperationRequest(datastoreName)
153 when: 'request resource data for data operation is called'
154 objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
155 then: 'request resource data for data operation returns expected response'
156 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
157 where: 'the following data stores are used'
158 datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
161 def 'Getting Yang Resources.'() {
162 when: 'yang resources is called'
163 objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
164 then: 'CPS module services is invoked for the correct dataspace and cm handle'
165 1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
168 def 'Get a cm handle.'() {
169 given: 'the system returns a yang modelled cm handle'
170 def dmiServiceName = 'some service name'
171 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
172 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
173 lastUpdateTime: 'some-timestamp',
174 dataSyncEnabled: false,
175 dataStores: dataStores())
176 def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
177 def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
178 def moduleSetTag = 'some-module-set-tag'
179 def alternateId = 'some-alternate-id'
180 def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
181 dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
182 moduleSetTag: moduleSetTag, alternateId: alternateId)
183 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
184 when: 'getting cm handle details for a given cm handle id from ncmp service'
185 def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
186 then: 'the result is a ncmpServiceCmHandle'
187 result.class == NcmpServiceCmHandle.class
188 and: 'the cm handle contains the cm handle id'
189 result.cmHandleId == 'some-cm-handle'
190 and: 'the cm handle contains the alternate id'
191 result.alternateId == 'some-alternate-id'
192 and: 'the cm handle contains the module-set-tag'
193 result.moduleSetTag == 'some-module-set-tag'
194 and: 'the cm handle contains the DMI Properties'
195 result.dmiProperties ==[ Book:'Romance Novel' ]
196 and: 'the cm handle contains the public Properties'
197 result.publicProperties == [ "Public Book":'Public Romance Novel' ]
198 and: 'the cm handle contains the cm handle composite state'
199 result.compositeState == compositeState
202 def 'Get cm handle public properties'() {
203 given: 'a yang modelled cm handle'
204 def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
205 def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
206 def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
207 and: 'the system returns this yang modelled cm handle'
208 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
209 when: 'getting cm handle public properties for a given cm handle id from ncmp service'
210 def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
211 then: 'the result returns the correct data'
212 result == [ 'public prop' : 'some public prop' ]
215 def 'Execute cm handle id search for inventory'() {
216 given: 'a ConditionApiProperties object'
217 def conditionProperties = new ConditionProperties()
218 conditionProperties.conditionName = 'hasAllProperties'
219 conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
220 def conditionServiceProps = new CmHandleQueryServiceParameters()
221 conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
222 and: 'the system returns an set of cmHandle ids'
223 mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
224 when: 'getting cm handle id set for a given dmi property'
225 def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
226 then: 'the result returns the correct 2 elements'
227 assert result.size() == 2
228 assert result.contains('cmHandle1')
229 assert result.contains('cmHandle2')
232 def 'Get cm handle composite state'() {
233 given: 'a yang modelled cm handle'
234 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
235 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
236 lastUpdateTime: 'some-timestamp',
237 dataSyncEnabled: false,
238 dataStores: dataStores())
239 def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
240 def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
241 def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
242 and: 'the system returns this yang modelled cm handle'
243 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
244 when: 'getting cm handle composite state for a given cm handle id from ncmp service'
245 def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
246 then: 'the result returns the correct data'
247 result == compositeState
250 def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
251 given: 'cpsDataService returns valid datanode'
252 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
253 when: 'get resource data is called'
254 objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
255 'testResourceId', UPDATE,
256 '{some-json}', 'application/json', NO_AUTH_HEADER)
257 then: 'DMI called with correct data'
258 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
259 UPDATE, '{some-json}', 'application/json', NO_AUTH_HEADER)
260 >> { new ResponseEntity<>(HttpStatus.OK) }
263 def 'Verify modules and create anchor params.'() {
264 given: 'dmi plugin registration return created cm handles'
265 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
266 dmiDataPlugin: 'service2')
267 dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
268 mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
269 and: 'no rejected cm handles because of alternate ids'
270 mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
271 when: 'parse and create cm handle in dmi registration then sync module'
272 mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
273 objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
274 then: 'system persists the cm handle state'
275 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
277 def cmHandleStatePerCmHandle = (args[0] as Collection)
278 cmHandleStatePerCmHandle.each {
279 assert it.id == 'test-cm-handle-id'
285 def 'Execute cm handle id search'() {
286 given: 'valid CmHandleQueryApiParameters input'
287 def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
288 def conditionApiProperties = new ConditionApiProperties()
289 conditionApiProperties.conditionName = 'hasAllModules'
290 conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
291 cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
292 and: 'query cm handle method return with a data node list'
293 mockCpsCmHandlerQueryService.queryCmHandleIds(
294 spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
295 >> ['cm-handle-id-1']
296 when: 'execute cm handle search is called'
297 def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
298 then: 'result is the same collection as returned by the CPS Data Service'
299 assert result == ['cm-handle-id-1']
302 def 'Getting module definitions by module'() {
303 when: 'get module definitions is performed with module name'
304 objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
305 then: 'ncmp inventory persistence service is invoked once with correct parameters'
306 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
309 def 'Getting module definitions by cm handle id'() {
310 when: 'get module definitions is performed with cm handle id'
311 objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
312 then: 'ncmp inventory persistence service is invoked once with correct parameter'
313 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
316 def 'Execute cm handle search'() {
317 given: 'valid CmHandleQueryApiParameters input'
318 def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
319 def conditionApiProperties = new ConditionApiProperties()
320 conditionApiProperties.conditionName = 'hasAllModules'
321 conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
322 cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
323 and: 'query cm handle method return with a data node list'
324 mockCpsCmHandlerQueryService.queryCmHandles(
325 spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
326 >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
327 when: 'execute cm handle search is called'
328 def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
329 then: 'result is the same collection as returned by the CPS Data Service'
330 assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
333 def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is #scenario'() {
334 given: 'an existing cm handle composite state'
335 def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
336 dataStores: CompositeState.DataStores.builder()
337 .operationalDataStore(CompositeState.Operational.builder()
338 .dataStoreSyncState(initialDataSyncState)
340 and: 'get cm handle state returns the composite state for the given cm handle id'
341 mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
342 when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
343 objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
344 then: 'the data sync enabled flag is set to #dataSyncEnabled'
345 compositeState.dataSyncEnabled == dataSyncEnabledFlag
346 and: 'the data store sync state is set to #expectedDataStoreSyncState'
347 compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
348 and: 'the cps data service to delete data nodes is invoked the expected number of times'
349 deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
350 and: 'the inventory persistence service to update node leaves is called with the correct values'
351 saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
352 where: 'the following data sync enabled flag is used'
353 scenario | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState || expectedDataStoreSyncState | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
354 'enabled' | true | false | DataStoreSyncState.NONE_REQUESTED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 1
355 'disabled' | false | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 0 | 1
356 'disabled where sync-state is currently SYNCHRONIZED' | false | true | DataStoreSyncState.SYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 1 | 1
357 'is set to existing flag state' | true | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 0
360 def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
361 given: 'a cm handle composite state'
362 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
363 and: 'get cm handle state returns the composite state for the given cm handle id'
364 mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
365 when: 'set data sync enabled is called with the data sync enabled flag set to true'
366 objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
367 then: 'the expected exception is thrown'
369 and: 'the inventory persistence service to update node leaves is not invoked'
370 0 * mockInventoryPersistence.saveCmHandleState(_, _)
373 def 'Get all cm handle IDs by DMI plugin identifier.' () {
374 given: 'cm handle queries service returns cm handles'
375 1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
376 when: 'cm handle Ids are requested with dmi plugin identifier'
377 def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
378 then: 'the result size is correct'
379 assert result.size() == 2
380 and: 'the result returns the correct details'
381 assert result.containsAll('cm-handle-1','cm-handle-2')
385 CompositeState.DataStores.builder()
386 .operationalDataStore(CompositeState.Operational.builder()
387 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
388 .lastSyncTime('some-timestamp').build()).build()
392 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
395 def getDataOperationRequest(datastore) {
396 def dataOperationRequest = new DataOperationRequest()
397 def dataOperationDefinitions = new ArrayList()
398 dataOperationDefinitions.add(getDataOperationDefinition(datastore))
399 dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
400 return dataOperationRequest
403 def getDataOperationDefinition(datastore) {
404 def dataOperationDefinition = new DataOperationDefinition()
405 dataOperationDefinition.setOperation("read")
406 dataOperationDefinition.setOperationId("operational-12")
407 dataOperationDefinition.setDatastore(datastore)
408 def targetIds = new ArrayList()
409 targetIds.add("some-cm-handle")
410 dataOperationDefinition.setCmHandleIds(targetIds)
411 return dataOperationDefinition