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 static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
27 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
28 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
29 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
30 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
31 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
32 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
33 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
35 import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
36 import org.onap.cps.ncmp.api.models.CmResourceAddress
37 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
38 import com.hazelcast.map.IMap
39 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
40 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
41 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
42 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
43 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
44 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
45 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
46 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
47 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
48 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
49 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
50 import org.onap.cps.ncmp.api.models.DataOperationDefinition
51 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
52 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
53 import org.onap.cps.ncmp.api.models.ConditionApiProperties
54 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
55 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
56 import org.onap.cps.ncmp.api.models.DataOperationRequest
57 import org.onap.cps.spi.exceptions.CpsException
58 import org.onap.cps.spi.model.ConditionProperties
59 import java.util.stream.Collectors
60 import org.onap.cps.utils.JsonObjectMapper
61 import com.fasterxml.jackson.databind.ObjectMapper
62 import org.onap.cps.api.CpsDataService
63 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
64 import org.onap.cps.spi.FetchDescendantsOption
65 import org.onap.cps.spi.model.DataNode
66 import org.springframework.http.HttpStatus
67 import org.springframework.http.ResponseEntity
68 import spock.lang.Specification
70 class NetworkCmProxyDataServiceImplSpec extends Specification {
72 def mockCpsDataService = Mock(CpsDataService)
73 def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
74 def mockDmiDataOperations = Mock(DmiDataOperations)
75 def nullNetworkCmProxyDataServicePropertyHandler = null
76 def mockInventoryPersistence = Mock(InventoryPersistence)
77 def mockCmHandleQueries = Mock(CmHandleQueries)
78 def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
79 def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
80 def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
81 def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
82 def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
83 def mockTrustLevelManager = Mock(TrustLevelManager)
84 def mockAlternateIdChecker = Mock(AlternateIdChecker)
87 def NO_REQUEST_ID = null
88 def NO_AUTH_HEADER = null
89 def OPTIONS_PARAM = '(a=1,b=2)'
90 def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
92 def objectUnderTest = new NetworkCmProxyDataServiceImpl(
93 spiedJsonObjectMapper,
94 mockDmiDataOperations,
95 nullNetworkCmProxyDataServicePropertyHandler,
96 mockInventoryPersistence,
98 mockCpsCmHandlerQueryService,
99 mockLcmEventsCmHandleStateHandler,
101 stubModuleSyncStartedOnCmHandles,
102 stubTrustLevelPerDmiPlugin,
103 mockTrustLevelManager,
104 mockAlternateIdChecker)
106 def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
108 def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])]
110 def 'Write resource data for pass-through running from DMI using POST.'() {
111 given: 'cpsDataService returns valid datanode'
113 when: 'write resource data is called'
114 objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
115 'testResourceId', CREATE,
116 '{some-json}', 'application/json', null)
117 then: 'DMI called with correct data'
118 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
119 CREATE, '{some-json}', 'application/json', null)
120 >> { new ResponseEntity<>(HttpStatus.CREATED) }
123 def 'Get resource data for from DMI.'() {
124 given: 'cpsDataService returns valid data node'
126 and: 'some cm resource address'
127 def cmResourceAddress = new CmResourceAddress('some datastore','some CM Handle', 'some resource Id')
128 and: 'get resource data from DMI is called'
129 mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >>
130 new ResponseEntity<>('dmi-response', HttpStatus.OK)
131 when: 'get resource data operational for the given cm resource address is called'
132 def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER)
133 then: 'DMI returns a json response'
134 assert response == 'dmi-response'
137 def 'Get resource data for operational (cached) data.'() {
138 given: 'CPS Data service returns some object(s)'
139 mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
140 and: 'a cm resource address for the same datastore, cm handle and resource id'
141 def cmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId')
142 when: 'get resource data is called'
143 def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, FetchDescendantsOption.OMIT_DESCENDANTS)
144 then: 'get resource data returns teh first object from the data service'
145 assert response == 'First Object'
148 def 'Execute (async) data operation for #datastoreName from DMI.'() {
149 given: 'cpsDataService returns valid data node'
150 def dataOperationRequest = getDataOperationRequest(datastoreName)
151 when: 'request resource data for data operation is called'
152 objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
153 then: 'request resource data for data operation returns expected response'
154 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
155 where: 'the following data stores are used'
156 datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
159 def 'Getting Yang Resources.'() {
160 when: 'yang resources is called'
161 objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
162 then: 'CPS module services is invoked for the correct dataspace and cm handle'
163 1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
166 def 'Get a cm handle.'() {
167 given: 'the system returns a yang modelled cm handle'
168 def dmiServiceName = 'some service name'
169 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
170 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
171 lastUpdateTime: 'some-timestamp',
172 dataSyncEnabled: false,
173 dataStores: dataStores())
174 def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
175 def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
176 def moduleSetTag = 'some-module-set-tag'
177 def alternateId = 'some-alternate-id'
178 def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
179 dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
180 moduleSetTag: moduleSetTag, alternateId: alternateId)
181 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
182 when: 'getting cm handle details for a given cm handle id from ncmp service'
183 def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
184 then: 'the result is a ncmpServiceCmHandle'
185 result.class == NcmpServiceCmHandle.class
186 and: 'the cm handle contains the cm handle id'
187 result.cmHandleId == 'some-cm-handle'
188 and: 'the cm handle contains the alternate id'
189 result.alternateId == 'some-alternate-id'
190 and: 'the cm handle contains the module-set-tag'
191 result.moduleSetTag == 'some-module-set-tag'
192 and: 'the cm handle contains the DMI Properties'
193 result.dmiProperties ==[ Book:'Romance Novel' ]
194 and: 'the cm handle contains the public Properties'
195 result.publicProperties == [ "Public Book":'Public Romance Novel' ]
196 and: 'the cm handle contains the cm handle composite state'
197 result.compositeState == compositeState
200 def 'Get cm handle public properties'() {
201 given: 'a yang modelled cm handle'
202 def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
203 def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
204 def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
205 and: 'the system returns this yang modelled cm handle'
206 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
207 when: 'getting cm handle public properties for a given cm handle id from ncmp service'
208 def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
209 then: 'the result returns the correct data'
210 result == [ 'public prop' : 'some public prop' ]
213 def 'Execute cm handle id search for inventory'() {
214 given: 'a ConditionApiProperties object'
215 def conditionProperties = new ConditionProperties()
216 conditionProperties.conditionName = 'hasAllProperties'
217 conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
218 def conditionServiceProps = new CmHandleQueryServiceParameters()
219 conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
220 and: 'the system returns an set of cmHandle ids'
221 mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
222 when: 'getting cm handle id set for a given dmi property'
223 def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
224 then: 'the result returns the correct 2 elements'
225 assert result.size() == 2
226 assert result.contains('cmHandle1')
227 assert result.contains('cmHandle2')
230 def 'Get cm handle composite state'() {
231 given: 'a yang modelled cm handle'
232 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
233 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
234 lastUpdateTime: 'some-timestamp',
235 dataSyncEnabled: false,
236 dataStores: dataStores())
237 def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
238 def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
239 def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
240 and: 'the system returns this yang modelled cm handle'
241 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
242 when: 'getting cm handle composite state for a given cm handle id from ncmp service'
243 def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
244 then: 'the result returns the correct data'
245 result == compositeState
248 def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
249 given: 'cpsDataService returns valid datanode'
250 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
251 when: 'get resource data is called'
252 objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
253 'testResourceId', UPDATE,
254 '{some-json}', 'application/json', NO_AUTH_HEADER)
255 then: 'DMI called with correct data'
256 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
257 UPDATE, '{some-json}', 'application/json', NO_AUTH_HEADER)
258 >> { new ResponseEntity<>(HttpStatus.OK) }
261 def 'Verify modules and create anchor params.'() {
262 given: 'dmi plugin registration return created cm handles'
263 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
264 dmiDataPlugin: 'service2')
265 dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
266 mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
267 and: 'no rejected cm handles because of alternate ids'
268 mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
269 when: 'parse and create cm handle in dmi registration then sync module'
270 mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
271 objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
272 then: 'system persists the cm handle state'
273 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
275 def cmHandleStatePerCmHandle = (args[0] as Collection)
276 cmHandleStatePerCmHandle.each {
277 assert it.id == 'test-cm-handle-id'
283 def 'Execute cm handle id search'() {
284 given: 'valid CmHandleQueryApiParameters input'
285 def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
286 def conditionApiProperties = new ConditionApiProperties()
287 conditionApiProperties.conditionName = 'hasAllModules'
288 conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
289 cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
290 and: 'query cm handle method return with a data node list'
291 mockCpsCmHandlerQueryService.queryCmHandleIds(
292 spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
293 >> ['cm-handle-id-1']
294 when: 'execute cm handle search is called'
295 def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
296 then: 'result is the same collection as returned by the CPS Data Service'
297 assert result == ['cm-handle-id-1']
300 def 'Getting module definitions by module'() {
301 when: 'get module definitions is performed with module name'
302 objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
303 then: 'ncmp inventory persistence service is invoked once with correct parameters'
304 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
307 def 'Getting module definitions by cm handle id'() {
308 when: 'get module definitions is performed with cm handle id'
309 objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
310 then: 'ncmp inventory persistence service is invoked once with correct parameter'
311 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
314 def 'Execute cm handle search'() {
315 given: 'valid CmHandleQueryApiParameters input'
316 def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
317 def conditionApiProperties = new ConditionApiProperties()
318 conditionApiProperties.conditionName = 'hasAllModules'
319 conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
320 cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
321 and: 'query cm handle method return with a data node list'
322 mockCpsCmHandlerQueryService.queryCmHandles(
323 spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
324 >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
325 when: 'execute cm handle search is called'
326 def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
327 then: 'result is the same collection as returned by the CPS Data Service'
328 assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
331 def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is #scenario'() {
332 given: 'an existing cm handle composite state'
333 def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
334 dataStores: CompositeState.DataStores.builder()
335 .operationalDataStore(CompositeState.Operational.builder()
336 .dataStoreSyncState(initialDataSyncState)
338 and: 'get cm handle state returns the composite state for the given cm handle id'
339 mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
340 when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
341 objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
342 then: 'the data sync enabled flag is set to #dataSyncEnabled'
343 compositeState.dataSyncEnabled == dataSyncEnabledFlag
344 and: 'the data store sync state is set to #expectedDataStoreSyncState'
345 compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
346 and: 'the cps data service to delete data nodes is invoked the expected number of times'
347 deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
348 and: 'the inventory persistence service to update node leaves is called with the correct values'
349 saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
350 where: 'the following data sync enabled flag is used'
351 scenario | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState || expectedDataStoreSyncState | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
352 'enabled' | true | false | DataStoreSyncState.NONE_REQUESTED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 1
353 'disabled' | false | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 0 | 1
354 'disabled where sync-state is currently SYNCHRONIZED' | false | true | DataStoreSyncState.SYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 1 | 1
355 'is set to existing flag state' | true | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 0
358 def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
359 given: 'a cm handle composite state'
360 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
361 and: 'get cm handle state returns the composite state for the given cm handle id'
362 mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
363 when: 'set data sync enabled is called with the data sync enabled flag set to true'
364 objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
365 then: 'the expected exception is thrown'
367 and: 'the inventory persistence service to update node leaves is not invoked'
368 0 * mockInventoryPersistence.saveCmHandleState(_, _)
371 def 'Get all cm handle IDs by DMI plugin identifier.' () {
372 given: 'cm handle queries service returns cm handles'
373 1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
374 when: 'cm handle Ids are requested with dmi plugin identifier'
375 def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
376 then: 'the result size is correct'
377 assert result.size() == 2
378 and: 'the result returns the correct details'
379 assert result.containsAll('cm-handle-1','cm-handle-2')
383 CompositeState.DataStores.builder()
384 .operationalDataStore(CompositeState.Operational.builder()
385 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
386 .lastSyncTime('some-timestamp').build()).build()
390 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
393 def getDataOperationRequest(datastore) {
394 def dataOperationRequest = new DataOperationRequest()
395 def dataOperationDefinitions = new ArrayList()
396 dataOperationDefinitions.add(getDataOperationDefinition(datastore))
397 dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
398 return dataOperationRequest
401 def getDataOperationDefinition(datastore) {
402 def dataOperationDefinition = new DataOperationDefinition()
403 dataOperationDefinition.setOperation("read")
404 dataOperationDefinition.setOperationId("operational-12")
405 dataOperationDefinition.setDatastore(datastore)
406 def targetIds = new ArrayList()
407 targetIds.add("some-cm-handle")
408 dataOperationDefinition.setCmHandleIds(targetIds)
409 return dataOperationDefinition