From 4a6e141c886dee974af4661db7d26792b5e75210 Mon Sep 17 00:00:00 2001 From: leventecsanyi Date: Mon, 17 Jun 2024 11:11:24 +0200 Subject: Spliting a data-job into sub-jobs for DMI Plugin - algorithm for create sub-job requests - added new method to DmiServiceUrlBuilder to get the write job url - created WriteOperationExaminer, DmiSubJobClient & testware Issue-ID: CPS-2142 Change-Id: I258d334ef346cd388341a1deb4078d24d8bdb7cc Signed-off-by: leventecsanyi --- .../ncmp/api/impl/DataJobServiceImplSpec.groovy | 46 +++++++++------- .../api/impl/DmiSubJobRequestHandlerSpec.groovy | 41 ++++++++++++++ .../ncmp/api/impl/WriteRequestExaminerSpec.groovy | 63 ++++++++++++++++++++++ .../cps/ncmp/utils/AlternateIdMatcherSpec.groovy | 4 +- 4 files changed, 132 insertions(+), 22 deletions(-) create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy (limited to 'cps-ncmp-service/src/test/groovy/org/onap') diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy index bef0adc9cb..9cee2bdbad 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy @@ -25,6 +25,8 @@ import ch.qos.logback.classic.Logger import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.core.read.ListAppender import org.onap.cps.ncmp.impl.datajobs.DataJobServiceImpl +import org.onap.cps.ncmp.impl.datajobs.DmiSubJobRequestHandler +import org.onap.cps.ncmp.impl.datajobs.WriteRequestExaminer import org.slf4j.LoggerFactory import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest @@ -33,9 +35,14 @@ import org.onap.cps.ncmp.api.datajobs.models.ReadOperation import org.onap.cps.ncmp.api.datajobs.models.WriteOperation import spock.lang.Specification -class DataJobServiceImplSpec extends Specification{ +class DataJobServiceImplSpec extends Specification { - def objectUnderTest = new DataJobServiceImpl() + def mockWriteRequestExaminer = Mock(WriteRequestExaminer) + def mockDmiSubJobRequestHandler = Mock(DmiSubJobRequestHandler) + + def objectUnderTest = new DataJobServiceImpl(mockDmiSubJobRequestHandler, mockWriteRequestExaminer) + + def myDataJobMetadata = new DataJobMetadata('', '', '') def logger = Spy(ListAppender) @@ -47,28 +54,27 @@ class DataJobServiceImplSpec extends Specification{ ((Logger) LoggerFactory.getLogger(DataJobServiceImpl.class)).detachAndStopAllAppenders() } - def '#operation data job request.'() { - given: 'data job metadata' - def dataJobMetadata = new DataJobMetadata('client-topic', 'application/vnd.3gpp.object-tree-hierarchical+json', 'application/3gpp-json-patch+json') - when: 'read/write data job request is processed' - if (operation == 'read') { - objectUnderTest.readDataJob('some-job-id', dataJobMetadata, new DataJobReadRequest([getWriteOrReadOperationRequest(operation)])) - } else { - objectUnderTest.writeDataJob('some-job-id', dataJobMetadata, new DataJobWriteRequest([getWriteOrReadOperationRequest(operation)])) - } + def 'Read data job request.'() { + when: 'read data job request is processed' + def readOperation = new ReadOperation('', '', '', [], [], '', '', 1) + objectUnderTest.readDataJob('my-job-id', myDataJobMetadata, new DataJobReadRequest([readOperation])) then: 'the data job id is correctly logged' def loggingEvent = logger.list[0] assert loggingEvent.level == Level.INFO - assert loggingEvent.formattedMessage.contains('data job id for ' + operation + ' operation is: some-job-id') - where: 'the following data job operations are used' - operation << ['read', 'write'] + assert loggingEvent.formattedMessage.contains('data job id for read operation is: my-job-id') } - def getWriteOrReadOperationRequest(operation) { - if (operation == 'write') { - return new WriteOperation('some/write/path', 'add', 'some-operation-id', 'some-value') - } - return new ReadOperation('some/read/path', 'read', 'some-operation-id', ['some-attrib-1'], ['some-field-1'], 'some-filter', 'some-scope-type', 1) + def 'Write data-job request.'() { + given: 'data job metadata and write request' + def dataJobWriteRequest = new DataJobWriteRequest([new WriteOperation('', '', '', null)]) + and: 'a map of producer key and dmi 3gpp write operation' + def dmiWriteOperationsPerProducerKey = [:] + when: 'write data job request is processed' + objectUnderTest.writeDataJob('my-job-id', myDataJobMetadata, dataJobWriteRequest) + then: 'the examiner service is called and a map is returned' + 1 * mockWriteRequestExaminer.splitDmiWriteOperationsFromRequest('my-job-id', dataJobWriteRequest) >> dmiWriteOperationsPerProducerKey + and: 'the dmi request handler is called with the result from the examiner' + 1 * mockDmiSubJobRequestHandler.sendRequestsToDmi('my-job-id', myDataJobMetadata, dmiWriteOperationsPerProducerKey) } def setupLogger() { @@ -77,4 +83,4 @@ class DataJobServiceImplSpec extends Specification{ setupLogger.addAppender(logger) logger.start() } -} +} \ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy new file mode 100644 index 0000000000..a1e0329792 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy @@ -0,0 +1,41 @@ +package org.onap.cps.ncmp.api.impl + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata +import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation +import org.onap.cps.ncmp.api.datajobs.models.ProducerKey +import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse +import org.onap.cps.ncmp.api.impl.client.DmiRestClient +import org.onap.cps.ncmp.api.impl.config.DmiProperties +import org.onap.cps.ncmp.api.impl.operations.OperationType +import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService +import org.onap.cps.ncmp.impl.datajobs.DmiSubJobRequestHandler +import org.onap.cps.utils.JsonObjectMapper +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import spock.lang.Specification + +class DmiSubJobRequestHandlerSpec extends Specification { + + def mockDmiRestClient = Mock(DmiRestClient) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def mockDmiProperties = Mock(DmiProperties) + def static NO_AUTH = null + def objectUnderTest = new DmiSubJobRequestHandler(mockDmiRestClient, mockDmiProperties, jsonObjectMapper) + + def 'Send a sub-job request to the DMI Plugin.'() { + given: 'a data job id, metadata and a map of producer keys and write operations to create a request' + def dataJobId = 'some-job-id' + def dataJobMetadata = new DataJobMetadata('', '', '') + def dmiWriteOperation = new DmiWriteOperation('', '', '', null, '', [:]) + def dmiWriteOperationsPerProducerKey = [new ProducerKey('', ''): [dmiWriteOperation]] + def response = new ResponseEntity<>(new SubJobWriteResponse('my-sub-job-id', '', ''), HttpStatus.OK) + when: 'sending request to DMI invoked' + objectUnderTest.sendRequestsToDmi(dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey) + then: 'the dmi rest client is called' + 1 * mockDmiRestClient.postOperationWithJsonData(RequiredDmiService.DATA, _, _, OperationType.CREATE, NO_AUTH) >> response + and: 'the result contains the expected sub-job write responses' + def result = response.body + assert result.subJobId() == 'my-sub-job-id' + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy new file mode 100644 index 0000000000..d5d6339422 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy @@ -0,0 +1,63 @@ + +package org.onap.cps.ncmp.impl.datajobs + +import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest +import org.onap.cps.ncmp.api.datajobs.models.WriteOperation +import org.onap.cps.ncmp.utils.AlternateIdMatcher +import org.onap.cps.spi.model.DataNode +import spock.lang.Specification + +class WriteRequestExaminerSpec extends Specification { + + def mockAlternateIdMatcher = Mock(AlternateIdMatcher) + def objectUnderTest = new WriteRequestExaminer(mockAlternateIdMatcher) + + def setup() { + def ch1 = new DataNode(leaves: [id: 'ch1', 'dmi-service-name': 'dmiA', 'data-producer-identifier': 'p1']) + def ch2 = new DataNode(leaves: [id: 'ch2', 'dmi-service-name': 'dmiA', 'data-producer-identifier': 'p1']) + def ch3 = new DataNode(leaves: [id: 'ch3', 'dmi-service-name': 'dmiA', 'data-producer-identifier': 'p2']) + def ch4 = new DataNode(leaves: [id: 'ch4', 'dmi-service-name': 'dmiB', 'data-producer-identifier': 'p1']) + mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn1', '/') >> ch1 + mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn2', '/') >> ch2 + mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn3', '/') >> ch3 + mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn4', '/') >> ch4 + } + + def 'Create a map of dmi write requests per producer key with #scenario.'() { + given: 'a write request with some write operations' + def writeOperations = writeOperationFdns.collect { + new WriteOperation(it, '', '', null) + } + and: 'operations are wrapped in a write request' + def dataJobWriteRequest = new DataJobWriteRequest(writeOperations) + when: 'the DMI write operations are split from the request' + def dmiWriteOperationsPerProducerKey = objectUnderTest.splitDmiWriteOperationsFromRequest('some id', dataJobWriteRequest) + then: 'we get the expected number of keys and values.' + def producerKeysAsStrings = dmiWriteOperationsPerProducerKey.keySet().collect { + it.toString() + } + assert producerKeysAsStrings.size() == expectedKeys.size() + assert expectedKeys.containsAll(producerKeysAsStrings) + where: + scenario | writeOperationFdns || expectedKeys + 'one fdn' | ['fdn1'] || ['dmiA#p1'] + 'a duplicated target' | ['fdn1','fdn1'] || ['dmiA#p1'] + 'two different targets' | ['fdn1','fdn2'] || ['dmiA#p1'] + 'two different targets and different producer keys' | ['fdn1','fdn3'] || ['dmiA#p1', 'dmiA#p2'] + 'two different targets and different DMI services' | ['fdn1','fdn4'] || ['dmiA#p1', 'dmiB#p1'] + 'many targets with different dmi service names and producer keys' | ['fdn1', 'fdn2', 'fdn3', 'fdn4'] || ['dmiA#p1', 'dmiA#p2', 'dmiB#p1'] + } + + def 'Validate the ordering of the created sub jobs.'() { + given: 'a few write operations for the same producer' + def writeOperations = (1..3).collect { + new WriteOperation('fdn1', '', it.toString(), null) + } + and: 'operation is wrapped in a write request' + def dataJobWriteRequest = new DataJobWriteRequest(writeOperations) + when: 'the DMI write operations are split from the request' + def dmiWriteOperations = objectUnderTest.splitDmiWriteOperationsFromRequest('some id', dataJobWriteRequest).values().iterator().next() + then: 'we get the operation ids in the expected order.' + assert dmiWriteOperations.operationId == ['1', '2', '3'] + } +} \ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy index 720a7e7e9f..3422610450 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy @@ -40,7 +40,7 @@ class AlternateIdMatcherSpec extends Specification { def 'Finding longest alternate id matches.'() { expect: 'querying for alternate id a matching result found' - assert objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(targetAlternateId, '/') != null + assert objectUnderTest.getCmHandleDataNodeByLongestMatchingAlternateId(targetAlternateId, '/') != null where: 'the following parameters are used' scenario | targetAlternateId 'exact match' | '/a/b' @@ -51,7 +51,7 @@ class AlternateIdMatcherSpec extends Specification { def 'Attempt to find longest alternate id match without any matches.'() { when: 'attempt to find alternateId' - objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(targetAlternateId, '/') + objectUnderTest.getCmHandleDataNodeByLongestMatchingAlternateId(targetAlternateId, '/') then: 'no alternate id match found exception thrown' def thrown = thrown(NoAlternateIdMatchFoundException) and: 'the exception has the relevant details from the error response' -- cgit 1.2.3-korg