summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/api/swagger/openapi-datajob.yaml214
-rw-r--r--openapi/openapi-datajob.yml203
-rw-r--r--pom.xml46
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java2
-rw-r--r--src/test/groovy/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutorIntegrationSpec.groovy24
-rw-r--r--src/test/groovy/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumerSpec.groovy11
6 files changed, 494 insertions, 6 deletions
diff --git a/docs/api/swagger/openapi-datajob.yaml b/docs/api/swagger/openapi-datajob.yaml
new file mode 100644
index 00000000..04a5bf1d
--- /dev/null
+++ b/docs/api/swagger/openapi-datajob.yaml
@@ -0,0 +1,214 @@
+openapi: 3.0.3
+info:
+ description: Support datajobs through one or more subjob for each DMI and Data Producer
+ Identifier combination
+ title: NCMP Data Subjob API
+ version: 1.0.0
+servers:
+ - url: /dmi
+tags:
+ - description: DMI plugin rest apis
+ name: dmi-datajob
+paths:
+ /v1/dataJob/{requestId}:
+ post:
+ description: Create a read request
+ operationId: createReadRequest
+ parameters:
+ - description: Identifier for the overall Datajob
+ explode: false
+ in: path
+ name: requestId
+ required: true
+ schema:
+ example: some-identifier
+ type: string
+ style: simple
+ requestBody:
+ content:
+ application/3gpp-json-patch+json:
+ schema:
+ $ref: '#/components/schemas/SubjobRequest'
+ description: Operation body
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/createReadRequest_200_response'
+ description: Response for subjob
+ tags:
+ - dmi-datajob
+components:
+ parameters:
+ requestIdInPath:
+ description: Identifier for the overall Datajob
+ explode: false
+ in: path
+ name: requestId
+ required: true
+ schema:
+ example: some-identifier
+ type: string
+ style: simple
+ schemas:
+ SubjobRequest:
+ properties:
+ dataAcceptType:
+ description: Defines the data response accept type
+ example: application/vnd.3gpp.object-tree-hierarchical+json
+ type: string
+ dataContentType:
+ description: Defines the data request content type
+ example: application/3gpp-json-patch+json
+ type: string
+ dataProducerId:
+ description: ID of the producer registered by DMI for the paths in the operations
+ in this request
+ example: my-data-producer-identifier
+ type: string
+ data:
+ $ref: '#/components/schemas/SubjobRequest_data'
+ required:
+ - data
+ - dataProducerId
+ type: object
+ ReadOperation:
+ example:
+ op: read
+ operationId: 1
+ path: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003/GNBCUCPFunction=2
+ attributes: userLabel
+ scope:
+ scopeTyp: BASE_ONLY
+ items:
+ $ref: '#/components/schemas/ReadOperation_inner'
+ type: array
+ WriteOperation:
+ example:
+ op: add
+ path: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003/GNBCUCPFunction=1/EUtraNetwork=1/EUtranFrequency=12
+ value:
+ id: 12
+ attributes:
+ userLabel: label12
+ items:
+ $ref: '#/components/schemas/WriteOperation_inner'
+ type: array
+ CmHandleProperties:
+ description: Private properties of the cm handle for the given path
+ type: object
+ Resource:
+ properties:
+ id:
+ description: Identifier of the resource object
+ example: resource-identifier
+ type: string
+ attributes:
+ additionalProperties:
+ example: "userLabel: label11"
+ type: string
+ description: Key value map representing the objects class attributes and
+ values
+ type: object
+ type: object
+ ActionParameters:
+ additionalProperties:
+ type: string
+ description: The input of the action in the form of key value pairs
+ type: object
+ Object:
+ type: object
+ createReadRequest_200_response:
+ example:
+ dataProducerJobId: dataProducerJobId
+ properties:
+ dataProducerJobId:
+ description: The data job ID.
+ type: string
+ type: object
+ SubjobRequest_data:
+ oneOf:
+ - $ref: '#/components/schemas/ReadOperation'
+ - $ref: '#/components/schemas/WriteOperation'
+ ReadOperation_inner:
+ properties:
+ path:
+ description: Defines the resource on which operation is executed
+ example: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003
+ type: string
+ op:
+ description: Describes the operation to execute
+ example: read
+ type: string
+ operationId:
+ description: Unique identifier for the operation within the request
+ example: "1"
+ type: string
+ attributes:
+ description: This parameter specifies the attributes of the scoped resources
+ that are returned
+ items:
+ example: cellId
+ type: string
+ type: array
+ fields:
+ description: This parameter specifies the attribute fields of the scoped
+ resources that are returned
+ items:
+ type: string
+ type: array
+ filter:
+ description: This parameter is used to filter the scoped Managed Objects.
+ Only Managed Objects passing the filter criteria will be fetched
+ example: NRCellDU/attributes/administrativeState==LOCKED
+ type: string
+ scopeType:
+ description: ScopeType selects MOs depending on relationships with Base
+ Managed Object
+ example: BASE_ONLY
+ type: string
+ scopeLevel:
+ description: Only used when the scope type is BASE_NTH_LEVEL to specify
+ amount of levels to search
+ example: 0
+ type: integer
+ moduleSetTag:
+ description: Module set identifier
+ example: my-module-set-tag
+ type: string
+ cmHandleProperties:
+ description: Private properties of the cm handle for the given path
+ type: object
+ required:
+ - op
+ - path
+ type: object
+ WriteOperation_inner_value:
+ description: Value dependent on the op specified. Resource for an add. Object
+ for a replace. ActionParameters for an action.
+ oneOf:
+ - $ref: '#/components/schemas/Resource'
+ - $ref: '#/components/schemas/ActionParameters'
+ - $ref: '#/components/schemas/Object'
+ type: object
+ WriteOperation_inner:
+ properties:
+ path:
+ description: Defines the resource on which operation is executed
+ example: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003
+ type: string
+ op:
+ description: Describes the operation to execute
+ example: add
+ type: string
+ operationId:
+ description: Unique identifier for the operation within the request
+ example: "1"
+ type: string
+ value:
+ $ref: '#/components/schemas/WriteOperation_inner_value'
+ required:
+ - op
+ - path
+ type: object
diff --git a/openapi/openapi-datajob.yml b/openapi/openapi-datajob.yml
new file mode 100644
index 00000000..b572ff3e
--- /dev/null
+++ b/openapi/openapi-datajob.yml
@@ -0,0 +1,203 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2024 Nordix Foundation
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+openapi: 3.0.3
+info:
+ title: NCMP Data Subjob API
+ description: Support datajobs through one or more subjob for each DMI and Data Producer Identifier combination
+ version: 1.0.0
+servers:
+ - url: /dmi
+tags:
+ - description: DMI plugin rest apis
+ name: dmi-datajob
+paths:
+ /v1/dataJob/{requestId}:
+ post:
+ description: Create a read request
+ operationId: createReadRequest
+ parameters:
+ - $ref: '#/components/parameters/requestIdInPath'
+ requestBody:
+ description: Operation body
+ content:
+ application/3gpp-json-patch+json:
+ schema:
+ $ref: '#/components/schemas/SubjobRequest'
+ tags:
+ - dmi-datajob
+ responses:
+ "200":
+ description: Response for subjob
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ dataProducerJobId:
+ type: string
+ description: The data job ID.
+components:
+ parameters:
+ requestIdInPath:
+ description: Identifier for the overall Datajob
+ in: path
+ name: requestId
+ required: true
+ schema:
+ example: some-identifier
+ type: string
+ schemas:
+ SubjobRequest:
+ type: object
+ required:
+ - dataProducerId
+ - data
+ properties:
+ dataAcceptType:
+ description: Defines the data response accept type
+ example: application/vnd.3gpp.object-tree-hierarchical+json
+ type: string
+ dataContentType:
+ description: Defines the data request content type
+ example: application/3gpp-json-patch+json
+ type: string
+ dataProducerId:
+ description: ID of the producer registered by DMI for the paths in the operations in this request
+ example: my-data-producer-identifier
+ type: string
+ data:
+ oneOf:
+ - $ref: '#/components/schemas/ReadOperation'
+ - $ref: '#/components/schemas/WriteOperation'
+ ReadOperation:
+ example:
+ op: read
+ operationId: 1
+ path: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003/GNBCUCPFunction=2
+ attributes: userLabel
+ scope:
+ scopeTyp: BASE_ONLY
+ type: array
+ items:
+ type: object
+ required:
+ - path
+ - op
+ properties:
+ path:
+ description: Defines the resource on which operation is executed
+ example: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003
+ type: string
+ op:
+ description: Describes the operation to execute
+ example: read
+ type: string
+ operationId:
+ description: Unique identifier for the operation within the request
+ example: 1
+ type: string
+ attributes:
+ description: This parameter specifies the attributes of the scoped resources that are returned
+ type: array
+ items:
+ example: cellId
+ type: string
+ fields:
+ description: This parameter specifies the attribute fields of the scoped resources that are returned
+ type: array
+ items:
+ type: string
+ filter:
+ description: This parameter is used to filter the scoped Managed Objects. Only Managed Objects passing the filter criteria will be fetched
+ example: NRCellDU/attributes/administrativeState==LOCKED
+ type: string
+ scopeType:
+ description: ScopeType selects MOs depending on relationships with Base Managed Object
+ example: BASE_ONLY
+ type: string
+ scopeLevel:
+ description: Only used when the scope type is BASE_NTH_LEVEL to specify amount of levels to search
+ example: 0
+ type: integer
+ moduleSetTag:
+ description: Module set identifier
+ example: my-module-set-tag
+ type: string
+ cmHandleProperties:
+ description: Private properties of the cm handle for the given path
+ $ref: '#/components/schemas/CmHandleProperties'
+ WriteOperation:
+ example:
+ op: add
+ path: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003/GNBCUCPFunction=1/EUtraNetwork=1/EUtranFrequency=12
+ value:
+ id: 12
+ attributes:
+ userLabel: label12
+ type: array
+ items:
+ type: object
+ required:
+ - path
+ - op
+ properties:
+ path:
+ description: Defines the resource on which operation is executed
+ example: SubNetwork=Europe/SubNetwork=Ireland/MeContext=NR03gNodeBRadio00003/ManagedElement=NR03gNodeBRadio00003
+ type: string
+ op:
+ description: Describes the operation to execute
+ example: add
+ type: string
+ operationId:
+ description: Unique identifier for the operation within the request
+ example: 1
+ type: string
+ value:
+ description: Value dependent on the op specified. Resource for an add. Object for a replace. ActionParameters for an action.
+ type: object
+ oneOf:
+ - $ref: '#/components/schemas/Resource'
+ - $ref: '#/components/schemas/ActionParameters'
+ - $ref: '#/components/schemas/Object'
+ CmHandleProperties:
+ description: Private properties of the cm handle for the given path
+ type: object
+ Resource:
+ type: object
+ properties:
+ id:
+ description: Identifier of the resource object
+ example: resource-identifier
+ type: string
+ attributes:
+ description: Key value map representing the objects class attributes and values
+ type: object
+ additionalProperties:
+ example: 'userLabel: label11'
+ type: string
+ ActionParameters:
+ description: The input of the action in the form of key value pairs
+ type: object
+ additionalProperties:
+ type: string
+ Object:
+ type: object
+
+
diff --git a/pom.xml b/pom.xml
index 1a21805e..8e20d8f7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,6 +1,6 @@
<!--
============LICENSE_START=======================================================
- Copyright (c) 2021-2023 Nordix Foundation.
+ Copyright (c) 2021-2024 Nordix Foundation.
Modifications Copyright (C) 2021 Bell Canada.
================================================================================
Licensed under the Apache License, Version 2.0 (the "License");
@@ -315,6 +315,7 @@
<version>6.6.0</version>
<executions>
<execution>
+ <id>dmi-code-gen</id>
<goals>
<goal>generate</goal>
</goals>
@@ -338,6 +339,30 @@
</configuration>
</execution>
<execution>
+ <id>dmi-datajob-code-gen</id>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ <configuration>
+ <inputSpec>${project.basedir}/openapi/openapi-datajob.yml</inputSpec>
+ <generatorName>spring</generatorName>
+ <generateSupportingFiles>false</generateSupportingFiles>
+ <invokerPackage>org.onap.cps.ncmp.dmi.rest.controller</invokerPackage>
+ <apiPackage>org.onap.cps.ncmp.dmi.rest.api</apiPackage>
+ <modelPackage>org.onap.cps.ncmp.dmi.model</modelPackage>
+ <generateAliasAsModel>true</generateAliasAsModel>
+ <configOptions>
+ <sourceFolder>src/gen/java</sourceFolder>
+ <dateLibrary>java11</dateLibrary>
+ <interfaceOnly>true</interfaceOnly>
+ <useTags>true</useTags>
+ <useSpringBoot3>true</useSpringBoot3>
+ <openApiNullable>false</openApiNullable>
+ <skipDefaultInterface>true</skipDefaultInterface>
+ </configOptions>
+ </configuration>
+ </execution>
+ <execution>
<id>openapi-yaml-gen</id>
<goals>
<goal>generate</goal>
@@ -346,6 +371,23 @@
<configuration>
<inputSpec>${project.basedir}/openapi/openapi.yml</inputSpec>
<generatorName>openapi-yaml</generatorName>
+ <configOptions>
+ <outputFile>openapi/openapi.yaml</outputFile>
+ </configOptions>
+ </configuration>
+ </execution>
+ <execution>
+ <id>openapi-datajob-yaml-gen</id>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ <phase>compile</phase>
+ <configuration>
+ <inputSpec>${project.basedir}/openapi/openapi-datajob.yml</inputSpec>
+ <generatorName>openapi-yaml</generatorName>
+ <configOptions>
+ <outputFile>openapi/openapi-datajob.yaml</outputFile>
+ </configOptions>
</configuration>
</execution>
</executions>
@@ -365,7 +407,7 @@
<resource>
<directory>${project.basedir}/target/generated-sources/openapi/openapi</directory>
<includes>
- <include>openapi.yaml</include>
+ <include>openapi*.yaml</include>
</includes>
</resource>
</resources>
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java b/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java
index ecfef6f3..da6243ca 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java
@@ -91,7 +91,7 @@ public class CmNotificationSubscriptionDmiInEventConsumer {
if (cmNotificationSubscriptionStatus.equals(CmNotificationSubscriptionStatus.ACCEPTED)) {
cmNotificationSubscriptionDmiOutEventData.setStatusCode("1");
cmNotificationSubscriptionDmiOutEventData.setStatusMessage("ACCEPTED");
- } else if (cmNotificationSubscriptionStatus.equals(CmNotificationSubscriptionStatus.REJECTED)) {
+ } else {
cmNotificationSubscriptionDmiOutEventData.setStatusCode("2");
cmNotificationSubscriptionDmiOutEventData.setStatusMessage("REJECTED");
}
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutorIntegrationSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutorIntegrationSpec.groovy
index 7ca2d54c..12ca05cf 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutorIntegrationSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutorIntegrationSpec.groovy
@@ -23,6 +23,7 @@ package org.onap.cps.ncmp.dmi.notifications.async
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.dmi.api.kafka.MessagingBaseSpec
import org.onap.cps.ncmp.dmi.exception.HttpClientRequestException
+import org.onap.cps.ncmp.dmi.model.DataAccessRequest
import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
import org.spockframework.spring.SpringBean
import org.springframework.boot.test.context.SpringBootTest
@@ -31,6 +32,7 @@ import org.springframework.test.annotation.DirtiesContext
import org.testcontainers.spock.Testcontainers
import java.time.Duration
+import java.util.function.Supplier
@SpringBootTest(classes = [AsyncTaskExecutor, DmiAsyncRequestResponseEventProducer])
@Testcontainers
@@ -42,6 +44,7 @@ class AsyncTaskExecutorIntegrationSpec extends MessagingBaseSpec {
new DmiAsyncRequestResponseEventProducer(kafkaTemplate)
def spiedObjectMapper = Spy(ObjectMapper)
+ def mockSupplier = Mock(Supplier)
def objectUnderTest = new AsyncTaskExecutor(cpsAsyncRequestResponseEventProducer)
@@ -83,4 +86,25 @@ class AsyncTaskExecutorIntegrationSpec extends MessagingBaseSpec {
assert event.getEventContent().getResponseCode() == '500'
}
+ def 'Execute an Async Task using asyncTaskExecutor and throw an error'() {
+ given: 'A task to be executed'
+ def requestId = '123456'
+ def operationEnum = DataAccessRequest.OperationEnum.CREATE
+ def timeOut = 100
+ when: 'AsyncTask has been executed'
+ objectUnderTest.executeAsyncTask(taskSupplierForFailingTask(), TEST_TOPIC, requestId, operationEnum, timeOut)
+ def records = kafkaConsumer.poll(Duration.ofMillis(1500))
+ then: 'the record received is the event sent'
+ def record = records.iterator().next()
+ DmiAsyncRequestResponseEvent event = spiedObjectMapper.readValue(record.value(), DmiAsyncRequestResponseEvent)
+ and: 'the status & code matches expected'
+ assert event.getEventContent().getResponseStatus() == 'Internal Server Error'
+ assert event.getEventContent().getResponseCode() == '500'
+
+ }
+
+ def taskSupplierForFailingTask() {
+ return () -> { throw new RuntimeException('original exception message') }
+ }
+
} \ No newline at end of file
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumerSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumerSpec.groovy
index 47953439..aa331c4b 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumerSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumerSpec.groovy
@@ -73,23 +73,28 @@ class CmNotificationSubscriptionDmiInEventConsumerSpec extends MessagingBaseSpec
objectUnderTest.dmiName = 'test-ncmp-dmi'
objectUnderTest.cmNotificationSubscriptionResponseTopic = testTopic
def correlationId = 'test-subscriptionId#test-ncmp-dmi'
- def cmSubscriptionDmiOutEventData = new Data(statusCode: '1', statusMessage: 'ACCEPTED')
+ def cmSubscriptionDmiOutEventData = new Data(statusCode: subscriptionStatusCode, statusMessage: subscriptionStatusMessage)
def subscriptionEventResponse =
new CmNotificationSubscriptionDmiOutEvent().withData(cmSubscriptionDmiOutEventData)
and: 'consumer has a subscription'
kafkaConsumer.subscribe([testTopic] as List<String>)
when: 'an event is published'
def eventKey = UUID.randomUUID().toString()
- objectUnderTest.createAndSendCmNotificationSubscriptionDmiOutEvent(eventKey, "subscriptionCreatedStatus", correlationId, CmNotificationSubscriptionStatus.ACCEPTED)
+ objectUnderTest.createAndSendCmNotificationSubscriptionDmiOutEvent(eventKey, "subscriptionCreatedStatus", correlationId, subscriptionAcceptanceType)
and: 'topic is polled'
def records = kafkaConsumer.poll(Duration.ofMillis(1500))
- then: 'poll returns one record'
+ then: 'poll returns one record and close kafkaConsumer'
assert records.size() == 1
def record = records.iterator().next()
+ kafkaConsumer.close()
and: 'the record value matches the expected event value'
def expectedValue = objectMapper.writeValueAsString(subscriptionEventResponse)
assert expectedValue == record.value
assert eventKey == record.key
+ where: 'given #scenario'
+ scenario | subscriptionAcceptanceType | subscriptionStatusCode | subscriptionStatusMessage
+ 'Subscription is Accepted' | CmNotificationSubscriptionStatus.ACCEPTED | '1' | 'ACCEPTED'
+ 'Subscription is Rejected' | CmNotificationSubscriptionStatus.REJECTED | '2' | 'REJECTED'
}
def 'Consume valid message.'() {