summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/async/batch-event-headers-1.0.0.json55
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/async/batch-event-schema-1.0.0.json67
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-header-v1.json (renamed from cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-headers-v1.json)2
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-v1.json91
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-header-v1.json56
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-schema-v1.json (renamed from cps-ncmp-events/src/main/resources/schemas/lcm-event-schema-v1.json)2
-rw-r--r--cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java10
-rw-r--r--cps-ncmp-rest/docs/openapi/components.yaml37
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp.yml6
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi.yml2
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java43
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java81
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java20
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java14
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java2
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/OperationNotSupportedException.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java)29
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/ResourceDataBatchRequestMapper.java41
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy94
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy4
-rw-r--r--cps-ncmp-service/pom.xml26
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java29
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java26
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/BatchRecordFilterStrategy.java50
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.java64
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java23
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java36
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java11
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java34
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java22
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java32
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java42
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java64
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java182
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java5
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java7
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java71
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java49
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java126
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java49
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java43
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy42
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy104
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy6
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy16
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorSpec.groovy18
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy40
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsServiceSpec.groovy34
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy43
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy16
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy80
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy14
-rw-r--r--cps-ncmp-service/src/test/resources/batchDataEvent.json46
-rw-r--r--cps-ncmp-service/src/test/resources/expectedLcmEvent.json2
-rw-r--r--cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json36
-rw-r--r--cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json41
-rw-r--r--dmi-plugin-stub/files/batchResponse.json4
-rw-r--r--dmi-plugin-stub/mappings/batchCmHandles.json2
59 files changed, 1809 insertions, 392 deletions
diff --git a/cps-ncmp-events/src/main/resources/schemas/async/batch-event-headers-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/async/batch-event-headers-1.0.0.json
new file mode 100644
index 0000000000..bbcadcd0f4
--- /dev/null
+++ b/cps-ncmp-events/src/main/resources/schemas/async/batch-event-headers-1.0.0.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events.async:batch-event-headers:1.0.0",
+ "$ref": "#/definitions/BatchEventHeaders",
+ "definitions": {
+ "BatchEventHeaders": {
+ "description": "The header information of the Batch event.",
+ "type": "object",
+ "javaType" : "org.onap.cps.ncmp.events.async.BatchEventHeadersV1",
+ "properties": {
+ "eventId": {
+ "description": "The unique id for identifying the event.",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The request id received by NCMP as an acknowledgement.",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The time of the event. It should be in RFC format ('yyyy-MM-dd'T'HH:mm:ss.SSSZ').",
+ "type": "string"
+ },
+ "eventTarget": {
+ "description": "The destination topic to forward the consumed event.",
+ "type": "string"
+ },
+ "eventSource": {
+ "description": "The source of the event.",
+ "type": "string"
+ },
+ "eventType": {
+ "description": "The type of the Batch event.",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The schema of the Batch event payload.",
+ "type": "string"
+ },
+ "eventSchemaVersion": {
+ "description": "The schema version of the Batch event payload.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "eventId",
+ "eventCorrelationId",
+ "eventTarget",
+ "eventType",
+ "eventSchema",
+ "eventSchemaVersion"
+ ],
+ "additionalProperties": false
+ }
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-events/src/main/resources/schemas/async/batch-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/async/batch-event-schema-1.0.0.json
new file mode 100644
index 0000000000..da836ff167
--- /dev/null
+++ b/cps-ncmp-events/src/main/resources/schemas/async/batch-event-schema-1.0.0.json
@@ -0,0 +1,67 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events.async:batch-event-schema:1.0.0",
+ "$ref": "#/definitions/BatchDataResponseEvent",
+ "definitions": {
+ "BatchDataResponseEvent": {
+ "description": "The payload of batch event.",
+ "type": "object",
+ "javaType" : "org.onap.cps.ncmp.events.async.BatchDataResponseEventV1",
+ "properties": {
+ "event": {
+ "description": "The payload content of the requested data.",
+ "type": "object",
+ "javaType" : "org.onap.cps.ncmp.events.async.BatchDataEvent",
+ "properties": {
+ "batch-responses": {
+ "description": "An array of batch responses which contains both success and failure",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "operationId": {
+ "description": "Used to distinguish multiple operations using same cmhandleId",
+ "type": "string"
+ },
+ "ids": {
+ "description": "Id's of the cmhandles",
+ "type": "array"
+ },
+ "status-code": {
+ "description": "which says success or failure (0-99) are for success and (100-199) are for failure",
+ "type": "string"
+ },
+ "status-message": {
+ "description": "Human readable message, Which says what the response has",
+ "type": "string"
+ },
+ "data": {
+ "description": "Contains the requested data response.",
+ "type": "object",
+ "existingJavaType": "java.lang.Object",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "operationId",
+ "ids",
+ "status-code",
+ "status-message"
+ ],
+ "additionalProperties": false
+ }
+ }
+ },
+ "required": [
+ "batch-responses"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "event"
+ ],
+ "additionalProperties": false
+ }
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-headers-v1.json b/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-header-v1.json
index caae82bb23..ea1e617c82 100644
--- a/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-headers-v1.json
+++ b/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-header-v1.json
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
- "$id": "urn:cps:org.onap.cps.ncmp.events:avc-event-headers-schema:v1",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:avc-event-header-schema:v1",
"$ref": "#/definitions/AvcEventHeader",
"definitions": {
"AvcEventHeader": {
diff --git a/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-v1.json b/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-v1.json
index 407551f4fd..7e975c9b93 100644
--- a/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-v1.json
+++ b/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-v1.json
@@ -3,16 +3,99 @@
"$id": "urn:cps:org.onap.cps.ncmp.events:avc-event-schema:v1",
"$ref": "#/definitions/AvcEvent",
"definitions": {
+ "Edit": {
+ "javaType": "org.onap.cps.ncmp.events.avc.v1.Edit",
+ "additionalProperties": false,
+ "properties": {
+ "edit-id": {
+ "type": "string"
+ },
+ "operation": {
+ "type": "string"
+ },
+ "target": {
+ "type": "string"
+ },
+ "value": {
+ "$ref": "#/definitions/Value"
+ }
+ },
+ "required": [
+ "edit-id",
+ "operation",
+ "target"
+ ]
+ },
+ "Value": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "attributes": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "existingJavaType": "java.util.Map<String,Object>",
+ "additionalProperties": false,
+ "properties": {
+ "isHoAllowed": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ },
"AvcEvent": {
"description": "The payload for AVC event.",
"type": "object",
- "javaType" : "org.onap.cps.ncmp.events.avc.v1.AvcEvent",
+ "javaType": "org.onap.cps.ncmp.events.avc.v1.AvcEvent",
"properties": {
"event": {
- "description": "The AVC event content.",
+ "description": "The AVC event content compliant with RFC8641 format",
"type": "object",
- "existingJavaType": "java.lang.Object",
- "additionalProperties": false
+ "additionalProperties": false,
+ "properties": {
+ "push-change-update": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "datastore-changes": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "ietf-yang-patch:yang-patch": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "patch-id": {
+ "type": "string"
+ },
+ "edit": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Edit"
+ }
+ }
+ },
+ "required": [
+ "patch-id",
+ "edit"
+ ]
+ }
+ },
+ "required": [
+ "ietf-yang-patch:yang-patch"
+ ]
+ }
+ },
+ "required": [
+ "datastore-changes"
+ ]
+ }
+ },
+ "required": [
+ "push-change-update"
+ ]
}
},
"required": [
diff --git a/cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-header-v1.json b/cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-header-v1.json
new file mode 100644
index 0000000000..8c9922ef7e
--- /dev/null
+++ b/cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-header-v1.json
@@ -0,0 +1,56 @@
+{
+
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.ncmp.cmhandle.lcm-event-header:v1",
+ "$ref": "#/definitions/LcmEventHeader",
+
+ "definitions": {
+ "LcmEventHeader": {
+ "description": "The header for LCM event",
+ "type": "object",
+ "javaType" : "org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader",
+ "properties": {
+ "eventId": {
+ "description": "The unique id identifying the event",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The id identifying the event",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The timestamp when original event occurred",
+ "type": "string"
+ },
+ "eventSource": {
+ "description": "The source of the event",
+ "type": "string"
+ },
+ "eventType": {
+ "description": "The type of the event",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The schema that this event adheres to",
+ "type": "string"
+ },
+ "eventSchemaVersion": {
+ "description": "The version of the schema that this event adheres to",
+ "type": "string"
+ }
+ },
+ "required": [
+ "eventId",
+ "eventCorrelationId",
+ "eventTime",
+ "eventSource",
+ "eventType",
+ "eventSchema",
+ "eventSchemaVersion",
+ "event"
+ ],
+ "additionalProperties": false
+ }
+
+ }
+}
diff --git a/cps-ncmp-events/src/main/resources/schemas/lcm-event-schema-v1.json b/cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-schema-v1.json
index 97c0fbee22..7006b78360 100644
--- a/cps-ncmp-events/src/main/resources/schemas/lcm-event-schema-v1.json
+++ b/cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-schema-v1.json
@@ -55,7 +55,7 @@
"LcmEvent": {
"description": "The payload for LCM event",
"type": "object",
- "javaType" : "org.onap.ncmp.cmhandle.event.lcm.LcmEvent",
+ "javaType" : "org.onap.cps.ncmp.events.lcm.v1.LcmEvent",
"properties": {
"eventId": {
"description": "The unique id identifying the event",
diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java
index 6e28dbc44c..7bd3acbf65 100644
--- a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java
+++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java
@@ -23,6 +23,7 @@ package org.onap.cps.ncmp.rest.stub.handlers;
import java.util.List;
import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
+import org.onap.cps.ncmp.rest.model.ResourceDataBatchRequest;
import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
import org.onap.cps.ncmp.rest.model.RestModuleReference;
import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
@@ -46,12 +47,9 @@ public interface NetworkCmProxyApiStubDefaultImpl extends NetworkCmProxyApi {
}
@Override
- default ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier,
- final String topicParamInQuery,
- final String datastoreName,
- final Object requestBody,
- final String optionsParamInQuery,
- final Boolean includeDescendants) {
+ default ResponseEntity<Object> getResourceDataForCmHandleBatch(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 7fc1063a12..2781f572f2 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -285,6 +285,43 @@ components:
properties:
state:
$ref: '#/components/schemas/CmHandleCompositeState'
+ # Batch Request Schemas
+ ResourceDataBatchRequest:
+ type: object
+ title: get resource data for given array of operations
+ properties:
+ operations:
+ type: array
+ items:
+ type: object
+ $ref: '#/components/schemas/BatchOperationDefinition'
+ description: contains batch request details
+ BatchOperationDefinition:
+ required:
+ - operation
+ - datastore
+ - operationId
+ properties:
+ operation:
+ type: string
+ example: 'read'
+ operationId:
+ type: string
+ example: '12'
+ datastore:
+ type: string
+ example: 'ncmp-datastore:passthrough-operational'
+ options:
+ type: string
+ example: '(fields=schemas/schema)'
+ resourceIdentifier:
+ type: string
+ example: 'parent/child'
+ targetIds:
+ type: array
+ items:
+ type: string
+ example: [ "da310eecdb8d44c2acc0ddaae01174b1","c748c58f8e0b438f9fd1f28370b17d47" ]
examples:
dataSampleRequest:
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index 2b70d94892..957a3b8735 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -202,17 +202,13 @@ getResourceDataForCmHandleBatch:
description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages.
operationId: getResourceDataForCmHandleBatch
parameters:
- - $ref: 'components.yaml#/components/parameters/datastoreName'
- - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
- $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery'
- - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery'
requestBody:
required: true
content:
application/json:
schema:
- type: object
+ $ref: 'components.yaml#/components/schemas/ResourceDataBatchRequest'
responses:
200:
description: OK
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index 5b4c0d3496..b63b568234 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -34,7 +34,7 @@ paths:
/v1/ch/{cm-handle}/data/ds/{datastore-name}:
$ref: 'ncmp.yml#/resourceDataForCmHandle'
- /v1/batch/data/ds/{datastore-name}:
+ /v1/data:
$ref: 'ncmp.yml#/getResourceDataForCmHandleBatch'
/v1/ch/{cm-handle}/data/ds/{datastore-name}/query:
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
index fca1d6310f..1b78fa0343 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
@@ -23,10 +23,12 @@
package org.onap.cps.ncmp.rest.controller;
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE;
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE;
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH;
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE;
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL;
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING;
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.DELETE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH;
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE;
import java.util.Collection;
import java.util.List;
@@ -44,8 +46,10 @@ import org.onap.cps.ncmp.rest.controller.handlers.NcmpCachedResourceRequestHandl
import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreRequestHandler;
import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler;
import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper;
+import org.onap.cps.ncmp.rest.mapper.ResourceDataBatchRequestMapper;
import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
+import org.onap.cps.ncmp.rest.model.ResourceDataBatchRequest;
import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
import org.onap.cps.ncmp.rest.model.RestModuleReference;
import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
@@ -72,6 +76,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
private final CmHandleStateMapper cmHandleStateMapper;
private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler;
private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler;
+ private final ResourceDataBatchRequestMapper resourceDataBatchRequestMapper;
/**
* Get resource data from datastore.
@@ -100,19 +105,11 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
@Override
- public ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier,
- final String topicParamInQuery,
- final String datastoreName,
- final Object requestBody,
- final String optionsParamInQuery,
- final Boolean includeDescendants) {
-
- final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler = getNcmpDatastoreRequestHandler(datastoreName);
-
- final List<String> cmHandleIds = jsonObjectMapper.convertJsonString(jsonObjectMapper.asJsonString(requestBody),
- List.class);
- return ncmpDatastoreRequestHandler.executeRequest(datastoreName, cmHandleIds, resourceIdentifier,
- optionsParamInQuery, topicParamInQuery, includeDescendants);
+ public ResponseEntity<Object> getResourceDataForCmHandleBatch(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest) {
+ return ncmpPassthroughResourceRequestHandler.executeRequest(topicParamInQuery,
+ resourceDataBatchRequestMapper.toResourceDataBatchRequest(resourceDataBatchRequest));
}
/**
@@ -134,7 +131,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final String optionsParamInQuery,
final String topicParamInQuery,
final Boolean includeDescendants) {
- validateDataStore(DatastoreType.OPERATIONAL, datastoreName);
+ validateDataStore(OPERATIONAL, datastoreName);
return ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, includeDescendants);
}
@@ -156,7 +153,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final Object requestBody,
final String contentType) {
- validateDataStore(DatastoreType.PASSTHROUGH_RUNNING, datastoreName);
+ validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
final Object responseObject = networkCmProxyDataService
.writeResourceDataPassThroughRunningForCmHandle(
@@ -182,7 +179,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final Object requestBody,
final String contentType) {
- validateDataStore(DatastoreType.PASSTHROUGH_RUNNING, datastoreName);
+ validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType);
@@ -206,7 +203,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final String cmHandle,
final Object requestBody,
final String contentType) {
- validateDataStore(DatastoreType.PASSTHROUGH_RUNNING, datastoreName);
+ validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType);
@@ -228,7 +225,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final String resourceIdentifier,
final String contentType) {
- validateDataStore(DatastoreType.PASSTHROUGH_RUNNING, datastoreName);
+ validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
resourceIdentifier, DELETE, NO_BODY, contentType);
@@ -381,7 +378,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
private NcmpDatastoreRequestHandler getNcmpDatastoreRequestHandler(final String datastoreName) {
- if (DatastoreType.OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) {
+ if (OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) {
return ncmpCachedResourceRequestHandler;
}
return ncmpPassthroughResourceRequestHandler;
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
index a32c462e76..a8ca13a752 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
@@ -20,12 +20,19 @@
package org.onap.cps.ncmp.rest.controller.handlers;
-import java.util.List;
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL;
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ;
+
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException;
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType;
+import org.onap.cps.ncmp.api.impl.operations.OperationType;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
+import org.onap.cps.ncmp.rest.exceptions.OperationNotSupportedException;
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
import org.onap.cps.ncmp.rest.util.TopicValidator;
import org.springframework.beans.factory.annotation.Value;
@@ -67,7 +74,7 @@ public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler
final boolean asyncResponseRequested = topicParamInQuery != null;
if (asyncResponseRequested && notificationFeatureEnabled) {
return executeAsyncTaskAndGetResponseEntity(datastoreName, cmHandleId, resourceIdentifier,
- optionsParamInQuery, topicParamInQuery, includeDescendants, false);
+ optionsParamInQuery, topicParamInQuery, includeDescendants);
}
if (asyncResponseRequested) {
@@ -98,26 +105,21 @@ public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler
}
/**
- * Executes synchronous/asynchronous request for batch of cm handles.
+ * Executes asynchronous request for batch of cm handles to resource data.
*
- * @param datastoreName the name of the datastore
- * @param cmHandleIds list of cm handles
- * @param resourceIdentifier the resource identifier
- * @param optionsParamInQuery the options param in query
- * @param topicParamInQuery the topic param in query
- * @param includeDescendants whether to include descendants or not
+ * @param topicParamInQuery the topic param in query
+ * @param resourceDataBatchRequest batch request details for resource data
* @return the response entity
*/
- public ResponseEntity<Object> executeRequest(final String datastoreName,
- final List<String> cmHandleIds,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final boolean includeDescendants) {
-
- return executeAsyncTaskAndGetResponseEntity(datastoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery,
- topicParamInQuery, includeDescendants, true);
-
+ public ResponseEntity<Object> executeRequest(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest) {
+ validateBatchRequest(topicParamInQuery, resourceDataBatchRequest);
+ if (!notificationFeatureEnabled) {
+ return ResponseEntity.ok(Map.of("status",
+ "Asynchronous request is unavailable as notification feature is currently disabled."));
+ }
+ return getRequestIdAndSendBatchRequestToDmiService(topicParamInQuery, resourceDataBatchRequest);
}
protected ResponseEntity<Object> executeTaskAsync(final String topicParamInQuery,
@@ -127,7 +129,6 @@ public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler
TopicValidator.validateTopicName(topicParamInQuery);
log.debug("Received Async request with id {}", requestId);
cpsNcmpTaskExecutor.executeTask(taskSupplier, timeOutInMilliSeconds);
-
return ResponseEntity.ok(Map.of("requestId", requestId));
}
@@ -136,25 +137,43 @@ public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler
}
private ResponseEntity<Object> executeAsyncTaskAndGetResponseEntity(final String datastoreName,
- final Object targetObject,
+ final String cmHandleId,
final String resourceIdentifier,
final String optionsParamInQuery,
final String topicParamInQuery,
- final boolean includeDescendants,
- final boolean isBulkRequest) {
+ final boolean includeDescendants) {
final String requestId = UUID.randomUUID().toString();
- final Supplier<Object> taskSupplier;
- if (isBulkRequest) {
- taskSupplier = getTaskSupplierForBulkRequest(datastoreName, (List<String>) targetObject,
- resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
- } else {
- taskSupplier = getTaskSupplierForGetRequest(datastoreName, targetObject.toString(), resourceIdentifier,
- optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
- }
+ final Supplier<Object> taskSupplier = getTaskSupplierForGetRequest(datastoreName, cmHandleId,
+ resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
if (taskSupplier == NO_OBJECT_SUPPLIER) {
return new ResponseEntity<>(Map.of("status", "Unable to execute request as "
+ "datastore is not implemented."), HttpStatus.NOT_IMPLEMENTED);
}
return executeTaskAsync(topicParamInQuery, requestId, taskSupplier);
}
+
+ private ResponseEntity<Object> getRequestIdAndSendBatchRequestToDmiService(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest) {
+ final String requestId = UUID.randomUUID().toString();
+ sendResourceDataBatchRequestAsynchronously(topicParamInQuery, resourceDataBatchRequest, requestId);
+ return ResponseEntity.ok(Map.of("requestId", requestId));
+ }
+
+ private void validateBatchRequest(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest) {
+ TopicValidator.validateTopicName(topicParamInQuery);
+ resourceDataBatchRequest.getBatchOperationDefinitions().forEach(batchOperationDetail -> {
+ if (OperationType.fromOperationName(batchOperationDetail.getOperation()) != READ) {
+ throw new OperationNotSupportedException(
+ batchOperationDetail.getOperation() + " operation not yet supported for target ids :"
+ + batchOperationDetail.getCmHandleIds());
+ } else if (DatastoreType.fromDatastoreName(batchOperationDetail.getDatastore()) == OPERATIONAL) {
+ throw new InvalidDatastoreException(batchOperationDetail.getDatastore()
+ + " datastore is not supported for target ids : "
+ + batchOperationDetail.getCmHandleIds());
+ }
+ });
+ }
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java
index 18e5a9f5ac..5c35818a3a 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java
@@ -20,10 +20,11 @@
package org.onap.cps.ncmp.rest.controller.handlers;
-import java.util.List;
import java.util.function.Supplier;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@@ -56,17 +57,14 @@ public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestH
datastoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
}
+ @Async
@Override
- public Supplier<Object> getTaskSupplierForBulkRequest(final String datastoreName,
- final List<String> cmHandleIds,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final boolean includeDescendants) {
+ public void sendResourceDataBatchRequestAsynchronously(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest,
+ final String requestId) {
+ networkCmProxyDataService.requestResourceDataForCmHandleBatch(topicParamInQuery, resourceDataBatchRequest,
+ requestId);
- return () -> networkCmProxyDataService.getResourceDataForCmHandleBatch(
- datastoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
}
-
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java
index 6d68f76802..937935bec4 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java
@@ -20,8 +20,8 @@
package org.onap.cps.ncmp.rest.controller.handlers;
-import java.util.List;
import java.util.function.Supplier;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
import org.onap.cps.spi.FetchDescendantsOption;
public interface TaskManagementDefaultHandler {
@@ -46,14 +46,10 @@ public interface TaskManagementDefaultHandler {
return NO_OBJECT_SUPPLIER;
}
- default Supplier<Object> getTaskSupplierForBulkRequest(final String datastoreName,
- final List<String> cmHandleIds,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final boolean includeDescendant) {
- return NO_OBJECT_SUPPLIER;
+ default void sendResourceDataBatchRequestAsynchronously(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest,
+ final String requestId) {
}
static FetchDescendantsOption getFetchDescendantsOption(final boolean includeDescendants) {
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
index 5faeee69fc..f459acec25 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
@@ -77,7 +77,7 @@ public class NetworkCmProxyRestExceptionHandler {
return wrapDmiErrorResponse(HttpStatus.BAD_GATEWAY, httpClientRequestException);
}
- @ExceptionHandler({DmiRequestException.class, DataValidationException.class,
+ @ExceptionHandler({DmiRequestException.class, DataValidationException.class, OperationNotSupportedException.class,
HttpMessageNotReadableException.class, InvalidTopicException.class, InvalidDatastoreException.class})
public static ResponseEntity<Object> handleDmiRequestExceptions(final Exception exception) {
return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/OperationNotSupportedException.java
index 796cef23d0..e1daf3df6f 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/OperationNotSupportedException.java
@@ -18,26 +18,15 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.operations;
+package org.onap.cps.ncmp.rest.exceptions;
-import com.fasterxml.jackson.annotation.JsonValue;
-
-public enum OperationEnum {
-
- READ("read"),
- CREATE("create"),
- UPDATE("update"),
- PATCH("patch"),
- DELETE("delete");
- private final String value;
-
- OperationEnum(final String value) {
- this.value = value;
- }
-
- @Override
- @JsonValue
- public String toString() {
- return String.valueOf(value);
+public class OperationNotSupportedException extends RuntimeException {
+ /**
+ * Instantiates a new not implemented operation exception.
+ *
+ * @param message the message
+ */
+ public OperationNotSupportedException(final String message) {
+ super(message);
}
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/ResourceDataBatchRequestMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/ResourceDataBatchRequestMapper.java
new file mode 100644
index 0000000000..d045e31610
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/ResourceDataBatchRequestMapper.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.mapper;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.onap.cps.ncmp.api.models.BatchOperationDefinition;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
+
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+ nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
+public interface ResourceDataBatchRequestMapper {
+
+ @Mapping(source = "operations", target = "batchOperationDefinitions")
+ ResourceDataBatchRequest toResourceDataBatchRequest(
+ org.onap.cps.ncmp.rest.model.ResourceDataBatchRequest resourceDataBatchRequest);
+
+ @Mapping(source = "targetIds", target = "cmHandleIds")
+ BatchOperationDefinition toBatchOperationDefinition(
+ org.onap.cps.ncmp.rest.model.BatchOperationDefinition batchOperationDefinition);
+}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
index fb411c054a..31e83aa7a1 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -32,11 +32,14 @@ import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
import org.onap.cps.ncmp.api.inventory.LockReasonCategory
+import org.onap.cps.ncmp.rest.model.BatchOperationDefinition
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
import org.onap.cps.ncmp.rest.controller.handlers.NcmpCachedResourceRequestHandler
import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
+import org.onap.cps.ncmp.rest.mapper.ResourceDataBatchRequestMapper
+import org.onap.cps.ncmp.rest.model.ResourceDataBatchRequest
import org.onap.cps.ncmp.rest.util.DeprecationHelper
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.model.ModuleDefinition
@@ -62,10 +65,10 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.DELETE
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
@@ -98,6 +101,9 @@ class NetworkCmProxyControllerSpec extends Specification {
CmHandleStateMapper cmHandleStateMapper = Mappers.getMapper(CmHandleStateMapper)
@SpringBean
+ ResourceDataBatchRequestMapper resourceDataBatchRequestMapper = Mappers.getMapper(ResourceDataBatchRequestMapper)
+
+ @SpringBean
CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy()
@SpringBean
@@ -113,7 +119,6 @@ class NetworkCmProxyControllerSpec extends Specification {
def ncmpBasePathV1
def requestBody = '{"some-key":"some-value"}'
- def bulkRequestBody = '["testCmHandle"]'
def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
@@ -200,44 +205,69 @@ class NetworkCmProxyControllerSpec extends Specification {
'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#'
}
- def 'Get (async) bulk resource data from dmi service.'() {
- given: 'bulk resource data url'
- def getUrl = "$ncmpBasePathV1/batch/data/ds/${datastore.datastoreName}" +
- "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic"
+ def 'Get (async) batch resource data from dmi service.'() {
+ given: 'batch resource data url'
+ def getUrl = "$ncmpBasePathV1/data?topic=my-topic-name"
+ def resourceDataBatchRequestJsonData = jsonObjectMapper.asJsonString(
+ getResourceDataBatchRequest("read", datastore.datastoreName))
+ def expectedDmiResourceDataBatchRequest
+ = jsonObjectMapper.convertJsonString(resourceDataBatchRequestJsonData, org.onap.cps.ncmp.api.models.ResourceDataBatchRequest.class)
when: 'post data resource request is performed'
def response = mvc.perform(
post(getUrl)
.contentType(MediaType.APPLICATION_JSON)
- .content(bulkRequestBody)
+ .content(resourceDataBatchRequestJsonData)
).andReturn().response
then: 'response status is Ok'
response.status == HttpStatus.OK.value()
and: 'async request id is generated'
assert response.contentAsString.contains("requestId")
then: 'wait a little to allow execution of service method by task executor (on separate thread)'
- Thread.sleep(100);
+ Thread.sleep(100)
then: 'the service has been invoked with the correct parameters '
- 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandleBatch(datastore.datastoreName, ['testCmHandle'],
- 'parent/child',
- '(a=1,b=2)',
- 'myTopic',
- _)
+ 1 * mockNetworkCmProxyDataService.requestResourceDataForCmHandleBatch('my-topic-name', expectedDmiResourceDataBatchRequest, _)
where: 'the following data stores are used'
datastore << [PASSTHROUGH_RUNNING, PASSTHROUGH_OPERATIONAL]
}
- def 'Get bulk resource data for non-supported #datastoreName from dmi service.'() {
- given: 'bulk resource data url'
- def getUrl = "$ncmpBasePathV1/batch/data/ds/ncmp-datastore:operational" +
- "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic"
+ def 'Get batch resource data for #scenario from dmi service.'() {
+ given: 'batch resource data url'
+ def getUrl = "$ncmpBasePathV1/data?topic=my-topic-name"
+ def resourceDataBatchRequestJsonData = jsonObjectMapper.asJsonString(
+ getResourceDataBatchRequest(operation, datastore))
+ when: 'post data resource request is performed'
+ def response = mvc.perform(
+ post(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(resourceDataBatchRequestJsonData)
+ ).andReturn().response
+ then: 'response status is BAD_REQUEST'
+ response.status == HttpStatus.BAD_REQUEST.value()
+ where: 'the following parameters are used'
+ scenario | datastore | operation
+ 'non-supported datastoreName' | OPERATIONAL.datastoreName | 'read'
+ 'non-supported operation (passthrough-running)' | PASSTHROUGH_RUNNING.datastoreName | 'create'
+ 'non-supported operation (passthrough-operational)' | PASSTHROUGH_OPERATIONAL.datastoreName | 'create'
+ }
+
+ def 'Get batch resource data when notification feature is disabled for datastore: #datastore.'() {
+ given: 'batch resource data url'
+ def getUrl = "$ncmpBasePathV1/data?topic=my-topic-name"
+ def resourceDataBatchRequestJsonData = jsonObjectMapper.asJsonString(
+ getResourceDataBatchRequest("read", datastore.datastoreName))
+ ncmpPassthroughResourceRequestHandler.notificationFeatureEnabled = false
when: 'post data resource request is performed'
def response = mvc.perform(
post(getUrl)
.contentType(MediaType.APPLICATION_JSON)
- .content(bulkRequestBody)
+ .content(resourceDataBatchRequestJsonData)
).andReturn().response
- then: 'response status code is 501 not implemented'
- response.status == HttpStatus.NOT_IMPLEMENTED.value()
+ then: 'response status is Ok'
+ response.status == HttpStatus.OK.value()
+ and: 'async request id is unavailable'
+ assert response.contentAsString == '{"status":"Asynchronous request is unavailable as notification feature is currently disabled."}'
+ where: 'the following data stores are used'
+ datastore << [PASSTHROUGH_RUNNING, PASSTHROUGH_OPERATIONAL]
}
def 'Query Resource Data from operational.'() {
@@ -656,5 +686,23 @@ class NetworkCmProxyControllerSpec extends Specification {
return assertContainsAll(response, expectedContent)
}
+ def getResourceDataBatchRequest(operation, datastore) {
+ def resourceDataBatchRequest = new ResourceDataBatchRequest()
+ def batchOperationDefinitions = new ArrayList()
+ batchOperationDefinitions.add(getBatchOperationDefinition(operation, datastore))
+ resourceDataBatchRequest.addOperationsItem(batchOperationDefinitions)
+ }
+
+ def getBatchOperationDefinition(operation, datastore) {
+ def batchOperationDefinition = new BatchOperationDefinition()
+ batchOperationDefinition.setOperation(operation)
+ batchOperationDefinition.setOperationId("operational-12")
+ batchOperationDefinition.setDatastore(datastore)
+ batchOperationDefinition.setOptions("some option")
+ batchOperationDefinition.setResourceIdentifier("some resource identifier")
+ batchOperationDefinition.addTargetIdsItem("some-cm-handle")
+ return batchOperationDefinition
+ }
+
}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
index f44d6c9907..a3afc5546f 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
@@ -33,6 +33,7 @@ import org.onap.cps.ncmp.rest.controller.handlers.NcmpCachedResourceRequestHandl
import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
+import org.onap.cps.ncmp.rest.mapper.ResourceDataBatchRequestMapper
import org.onap.cps.ncmp.rest.util.DeprecationHelper
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch
@@ -75,6 +76,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
CmHandleStateMapper cmHandleStateMapper = Mappers.getMapper(CmHandleStateMapper)
@SpringBean
+ ResourceDataBatchRequestMapper resourceDataBatchRequestMapper = Mappers.getMapper(ResourceDataBatchRequestMapper)
+
+ @SpringBean
CpsNcmpTaskExecutor stubbedCpsTaskExecutor = Stub()
@SpringBean
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index 0a732ef873..b87fe64366 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -38,6 +38,10 @@
</properties>
<dependencies>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>cps-service</artifactId>
</dependency>
@@ -67,6 +71,17 @@
</dependency>
<!-- T E S T - D E P E N D E N C I E S -->
<dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.junit.vintage</groupId>
+ <artifactId>junit-vintage-engine</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
@@ -86,16 +101,5 @@
<artifactId>spock</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.junit.vintage</groupId>
- <artifactId>junit-vintage-engine</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
</dependencies>
</project>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
index 05490d8fc9..046c78879b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
@@ -24,15 +24,15 @@
package org.onap.cps.ncmp.api;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
-import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
+import org.onap.cps.ncmp.api.impl.operations.OperationType;
import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters;
import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
@@ -83,22 +83,15 @@ public interface NetworkCmProxyDataService {
FetchDescendantsOption fetchDescendantsOption);
/**
- * Get resource data for given batch of cm handles using dmi.
+ * Get resource data for batch of cm handles using dmi.
*
- * @param datastoreName datastore name
- * @param cmHandleIds cm handle identifiers
- * @param resourceIdentifier resource identifier
- * @param optionsParamInQuery options query
- * @param topicParamInQuery topic name for (triggering) async responses
- * @param requestId unique requestId for async request
- * @return {@code Object} resource data
+ * @param topicParamInQuery topic name for (triggering) async responses
+ * @param resourceDataBatchRequest cm handle identifiers
*/
- Object getResourceDataForCmHandleBatch(String datastoreName,
- List<String> cmHandleIds,
- String resourceIdentifier,
- String optionsParamInQuery,
- String topicParamInQuery,
- String requestId);
+ void requestResourceDataForCmHandleBatch(String topicParamInQuery,
+ ResourceDataBatchRequest
+ resourceDataBatchRequest,
+ String requestId);
/**
@@ -106,14 +99,14 @@ public interface NetworkCmProxyDataService {
*
* @param cmHandleId cm handle identifier
* @param resourceIdentifier resource identifier
- * @param operation required operation
+ * @param operationType required operation type
* @param requestBody request body to create resource
* @param contentType content type in body
* @return {@code Object} return data
*/
Object writeResourceDataPassThroughRunningForCmHandle(String cmHandleId,
String resourceIdentifier,
- OperationEnum operation,
+ OperationType operationType,
String requestBody,
String contentType);
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
index e478b0053b..536775ec5c 100755
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
@@ -45,7 +45,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler;
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
-import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
+import org.onap.cps.ncmp.api.impl.operations.OperationType;
import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions;
import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
@@ -63,6 +63,7 @@ import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationErr
import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch;
import org.onap.cps.spi.exceptions.CpsException;
@@ -138,28 +139,21 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
@Override
- public Object getResourceDataForCmHandleBatch(final String datastoreName,
- final List<String> cmHandleIds,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId) {
- final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(datastoreName, cmHandleIds,
- resourceIdentifier,
- optionsParamInQuery,
- topicParamInQuery,
- requestId);
- return responseEntity.getBody();
+ public void requestResourceDataForCmHandleBatch(final String topicParamInQuery,
+ final ResourceDataBatchRequest
+ resourceDataBatchRequest,
+ final String requestId) {
+ dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, resourceDataBatchRequest, requestId);
}
@Override
public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId,
final String resourceIdentifier,
- final OperationEnum operation,
+ final OperationType operationType,
final String requestData,
final String dataType) {
- return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, operation,
- requestData, dataType);
+ return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier,
+ operationType, requestData, dataType);
}
@Override
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/BatchRecordFilterStrategy.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/BatchRecordFilterStrategy.java
new file mode 100644
index 0000000000..088e96564c
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/BatchRecordFilterStrategy.java
@@ -0,0 +1,50 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.listener.adapter.RecordFilterStrategy;
+
+/**
+ * Batch Record filter strategy, which helps to filter the consumer records.
+ *
+ */
+@Configuration
+public class BatchRecordFilterStrategy {
+
+ /**
+ * Filtering the consumer records based on the eventType header, It
+ * returns boolean, true means filter the consumer record and false
+ * means not filter the consumer record.
+ * @return boolean value.
+ */
+ @Bean
+ public RecordFilterStrategy<Object, Object> filterBatchDataResponseEvent() {
+ return consumedRecord -> {
+ final String headerValue = SerializationUtils
+ .deserialize(consumedRecord.headers().lastHeader("eventType").value());
+ return !(headerValue != null
+ && headerValue.startsWith("org.onap.cps.ncmp.events.async.BatchDataResponseEvent"));
+ };
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.java
new file mode 100644
index 0000000000..2a332d0037
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
+import org.onap.cps.ncmp.events.async.BatchDataResponseEventV1;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * Listener for cps-ncmp async batch events.
+ */
+@Component
+@Slf4j
+@RequiredArgsConstructor
+@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true)
+public class NcmpAsyncBatchEventConsumer {
+
+ private final EventsPublisher<BatchDataResponseEventV1> eventsPublisher;
+
+ /**
+ * Consume the BatchDataResponseEvent published by producer to topic 'async-m2m.topic'
+ * and publish the same to the client specified topic.
+ *
+ * @param batchEventConsumerRecord consuming event as a ConsumerRecord.
+ */
+ @KafkaListener(
+ topics = "${app.ncmp.async-m2m.topic}",
+ filter = "filterBatchDataResponseEvent",
+ groupId = "ncmp-batch-event-group",
+ properties = {"spring.json.value.default.type=org.onap.cps.ncmp.events.async.BatchDataResponseEventV1"})
+ public void consumeAndPublish(final ConsumerRecord<String, BatchDataResponseEventV1> batchEventConsumerRecord) {
+ log.info("Consuming event payload {} ...", batchEventConsumerRecord.value());
+ final String eventTarget = SerializationUtils
+ .deserialize(batchEventConsumerRecord.headers().lastHeader("eventTarget").value());
+ final String eventId = SerializationUtils
+ .deserialize(batchEventConsumerRecord.headers().lastHeader("eventId").value());
+ eventsPublisher.publishEvent(eventTarget, eventId, batchEventConsumerRecord.headers(),
+ batchEventConsumerRecord.value());
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
index 9d087806c0..136935ba53 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
@@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl.client;
import lombok.AllArgsConstructor;
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties;
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
-import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
+import org.onap.cps.ncmp.api.impl.operations.OperationType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -44,17 +44,17 @@ public class DmiRestClient {
* Sends POST operation to DMI with json body containing module references.
* @param dmiResourceUrl dmi resource url
* @param requestBodyAsJsonString json data body
- * @param operation the type of operation being executed (for error reporting only)
+ * @param operationType the type of operation being executed (for error reporting only)
* @return response entity of type String
*/
public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl,
final String requestBodyAsJsonString,
- final OperationEnum operation) {
+ final OperationType operationType) {
final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders()));
try {
return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
} catch (final HttpStatusCodeException httpStatusCodeException) {
- final String exceptionMessage = "Unable to " + operation.toString() + " resource data.";
+ final String exceptionMessage = "Unable to " + operationType.toString() + " resource data.";
throw new HttpClientRequestException(exceptionMessage, httpStatusCodeException.getResponseBodyAsString(),
httpStatusCodeException.getRawStatusCode());
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java
index 4c84629304..b0b091a2f6 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java
@@ -20,13 +20,16 @@
package org.onap.cps.ncmp.api.impl.events;
+import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.Headers;
+import org.apache.kafka.common.header.internals.RecordHeaders;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
+import org.springframework.util.SerializationUtils;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
@@ -70,6 +73,20 @@ public class EventsPublisher<T> {
eventFuture.addCallback(handleCallback(topicName));
}
+ /**
+ * Generic Event Publisher with headers.
+ *
+ * @param topicName valid topic name
+ * @param eventKey message key
+ * @param eventHeaders map of event headers
+ * @param event message payload
+ */
+ public void publishEvent(final String topicName, final String eventKey, final Map<String, Object> eventHeaders,
+ final T event) {
+
+ publishEvent(topicName, eventKey, convertToKafkaHeaders(eventHeaders), event);
+ }
+
private ListenableFutureCallback<SendResult<String, T>> handleCallback(final String topicName) {
return new ListenableFutureCallback<>() {
@Override
@@ -85,4 +102,10 @@ public class EventsPublisher<T> {
};
}
+ private Headers convertToKafkaHeaders(final Map<String, Object> eventMessageHeaders) {
+ final Headers eventHeaders = new RecordHeaders();
+ eventMessageHeaders.forEach((key, value) -> eventHeaders.add(key, SerializationUtils.serialize(value)));
+ return eventHeaders;
+ }
+
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java
new file mode 100644
index 0000000000..f7707d9f76
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.events.lcm;
+
+import org.mapstruct.Mapper;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader;
+
+@Mapper(componentModel = "spring")
+public interface LcmEventHeaderMapper {
+
+ /**
+ * Mapper for converting incoming {@link LcmEvent} to outgoing {@link LcmEventHeader}.
+ */
+
+ LcmEventHeader toLcmEventHeader(LcmEvent lcmEvent);
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java
index 9d518432ad..f42cd39d4d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java
@@ -43,7 +43,8 @@ import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.onap.cps.ncmp.api.inventory.CompositeStateUtils;
import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.ncmp.cmhandle.event.lcm.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@@ -76,7 +77,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
@Override
@Timed(value = "cps.ncmp.cmhandle.state.update.batch",
- description = "Time taken to update a batch of cm handle states")
+ description = "Time taken to update a batch of cm handle states")
public void updateCmHandleStateBatch(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) {
final Collection<CmHandleTransitionPair> cmHandleTransitionPairs =
prepareCmHandleTransitionBatch(cmHandleStatePerCmHandle);
@@ -106,9 +107,12 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
private void publishLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
final String cmHandleId = targetNcmpServiceCmHandle.getCmHandleId();
+ final LcmEventHeader lcmEventHeader =
+ lcmEventsCreator.populateLcmEventHeader(cmHandleId, targetNcmpServiceCmHandle,
+ existingNcmpServiceCmHandle);
final LcmEvent lcmEvent =
lcmEventsCreator.populateLcmEvent(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle);
- lcmEventsService.publishLcmEvent(cmHandleId, lcmEvent);
+ lcmEventsService.publishLcmEvent(cmHandleId, lcmEvent, lcmEventHeader);
}
private Collection<CmHandleTransitionPair> prepareCmHandleTransitionBatch(
@@ -221,6 +225,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
@Setter
@NoArgsConstructor
static class CmHandleTransitionPair {
+
private YangModelCmHandle currentYangModelCmHandle;
private YangModelCmHandle targetYangModelCmHandle;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java
index a72e664dcf..3c7c92b129 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java
@@ -23,13 +23,15 @@ package org.onap.cps.ncmp.api.impl.events.lcm;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.ncmp.cmhandle.event.lcm.Event;
-import org.onap.ncmp.cmhandle.event.lcm.LcmEvent;
-import org.onap.ncmp.cmhandle.event.lcm.Values;
+import org.onap.cps.ncmp.events.lcm.v1.Event;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader;
+import org.onap.cps.ncmp.events.lcm.v1.Values;
import org.springframework.stereotype.Component;
@@ -38,8 +40,11 @@ import org.springframework.stereotype.Component;
*/
@Slf4j
@Component
+@RequiredArgsConstructor
public class LcmEventsCreator {
+ private final LcmEventHeaderMapper lcmEventHeaderMapper;
+
/**
* Populate Lifecycle Management Event.
*
@@ -53,6 +58,20 @@ public class LcmEventsCreator {
return createLcmEvent(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle);
}
+ /**
+ * Populate Lifecycle Management Event Header.
+ *
+ * @param cmHandleId cm handle identifier
+ * @param targetNcmpServiceCmHandle target ncmp service cmhandle
+ * @param existingNcmpServiceCmHandle existing ncmp service cmhandle
+ * @return Populated LcmEventHeader
+ */
+ public LcmEventHeader populateLcmEventHeader(final String cmHandleId,
+ final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+ final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
+ return createLcmEventHeader(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle);
+ }
+
private LcmEvent createLcmEvent(final String cmHandleId, final NcmpServiceCmHandle targetNcmpServiceCmHandle,
final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
final LcmEventType lcmEventType =
@@ -63,6 +82,15 @@ public class LcmEventsCreator {
return lcmEvent;
}
+ private LcmEventHeader createLcmEventHeader(final String cmHandleId,
+ final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+ final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
+ final LcmEventType lcmEventType =
+ LcmEventsCreatorHelper.determineEventType(targetNcmpServiceCmHandle, existingNcmpServiceCmHandle);
+ final LcmEvent lcmEventWithHeaderInformation = lcmEventHeader(cmHandleId, lcmEventType);
+ return lcmEventHeaderMapper.toLcmEventHeader(lcmEventWithHeaderInformation);
+ }
+
private Event lcmEventPayload(final String eventCorrelationId, final NcmpServiceCmHandle targetNcmpServiceCmHandle,
final NcmpServiceCmHandle existingNcmpServiceCmHandle, final LcmEventType lcmEventType) {
final Event event = new Event();
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java
index 1322b7277f..d3b45d4a63 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java
@@ -34,7 +34,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.ncmp.cmhandle.event.lcm.Values;
+import org.onap.cps.ncmp.events.lcm.v1.Values;
/**
* LcmEventsCreatorHelper has helper methods to create LcmEvent.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java
index f258b45976..2e1b914b1d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java
@@ -21,10 +21,13 @@
package org.onap.cps.ncmp.api.impl.events.lcm;
import io.micrometer.core.annotation.Timed;
+import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
-import org.onap.ncmp.cmhandle.event.lcm.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader;
+import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.KafkaException;
import org.springframework.stereotype.Service;
@@ -39,6 +42,7 @@ import org.springframework.stereotype.Service;
public class LcmEventsService {
private final EventsPublisher<LcmEvent> eventsPublisher;
+ private final JsonObjectMapper jsonObjectMapper;
@Value("${app.lcm.events.topic:ncmp-events}")
private String topicName;
@@ -47,17 +51,19 @@ public class LcmEventsService {
private boolean notificationsEnabled;
/**
- * Publish the LcmEvent to the public topic.
+ * Publish the LcmEvent with header to the public topic.
*
- * @param cmHandleId Cm Handle Id
- * @param lcmEvent Lcm Event
+ * @param cmHandleId Cm Handle Id
+ * @param lcmEvent Lcm Event
+ * @param lcmEventHeader Lcm Event Header
*/
- @Timed(value = "cps.ncmp.lcm.events.publish",
- description = "Time taken to publish a LCM event")
- public void publishLcmEvent(final String cmHandleId, final LcmEvent lcmEvent) {
+ @Timed(value = "cps.ncmp.lcm.events.publish", description = "Time taken to publish a LCM event")
+ public void publishLcmEvent(final String cmHandleId, final LcmEvent lcmEvent, final LcmEventHeader lcmEventHeader) {
if (notificationsEnabled) {
try {
- eventsPublisher.publishEvent(topicName, cmHandleId, lcmEvent);
+ final Map<String, Object> lcmEventHeadersMap =
+ jsonObjectMapper.convertToValueType(lcmEventHeader, Map.class);
+ eventsPublisher.publishEvent(topicName, cmHandleId, lcmEventHeadersMap, lcmEvent);
} catch (final KafkaException e) {
log.error("Unable to publish message to topic : {} and cause : {}", topicName, e.getMessage());
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java
new file mode 100644
index 0000000000..17069098cb
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.exception;
+
+public class InvalidOperationException extends RuntimeException {
+ /**
+ * Instantiates a new invalid operation exception.
+ *
+ * @param message the message
+ */
+ public InvalidOperationException(final String message) {
+ super(message);
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java
new file mode 100644
index 0000000000..618da74543
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java
@@ -0,0 +1,42 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Map;
+import lombok.Builder;
+import lombok.Getter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Getter
+@Builder
+public class CmHandle {
+ private String id;
+
+ @JsonProperty("cmHandleProperties")
+ private Map<String, String> dmiProperties;
+
+ public static CmHandle buildCmHandleWithProperties(final String cmHandleId,
+ final Map<String, String> dmiProperties) {
+ return CmHandle.builder().id(cmHandleId).dmiProperties(dmiProperties).build();
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java
new file mode 100644
index 0000000000..76ad0f7b2e
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Builder;
+import lombok.Getter;
+import org.onap.cps.ncmp.api.models.BatchOperationDefinition;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Getter
+@Builder
+@JsonPropertyOrder({"operation", "operationId", "datastore", "options", "resourceIdentifier", "cmHandles"})
+public class DmiBatchOperation {
+
+ @JsonProperty("operation")
+ private OperationType operationType;
+ private String operationId;
+ private String datastore;
+ private String options;
+ private String resourceIdentifier;
+
+ private final List<CmHandle> cmHandles = new ArrayList<>();
+
+ /**
+ * Create and initialise a (outgoing) DMI batch operation.
+ *
+ * @param batchOperationDefinition batchOperationDefinition definition of incoming of batch request
+ * @return mapped dmi operation details
+ */
+ public static DmiBatchOperation buildDmiBatchRequestBodyWithoutCmHandles(
+ final BatchOperationDefinition batchOperationDefinition) {
+
+ return DmiBatchOperation.builder()
+ .operationType(OperationType.fromOperationName(batchOperationDefinition.getOperation()))
+ .operationId(batchOperationDefinition.getOperationId())
+ .datastore(DatastoreType.fromDatastoreName(batchOperationDefinition.getDatastore()).getDatastoreName())
+ .options(batchOperationDefinition.getOptions())
+ .resourceIdentifier(batchOperationDefinition.getResourceIdentifier())
+ .build();
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
index 1a3952306f..3e8d40a83b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
@@ -22,25 +22,28 @@
package org.onap.cps.ncmp.api.impl.operations;
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING;
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ;
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
import org.onap.cps.ncmp.api.impl.executor.TaskExecutor;
-import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
+import org.onap.cps.ncmp.api.impl.utils.ResourceDataBatchRequestUtils;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.CmHandleState;
import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.utils.JsonObjectMapper;
-import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
/**
* Operations class for DMI data.
@@ -50,7 +53,6 @@ import org.springframework.stereotype.Component;
public class DmiDataOperations extends DmiOperations {
private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L;
- private static final String NO_CM_HANDLE_ID = "";
public DmiDataOperations(final InventoryPersistence inventoryPersistence,
final JsonObjectMapper jsonObjectMapper,
@@ -89,39 +91,12 @@ public class DmiDataOperations extends DmiOperations {
}
/**
- * This method fetches the resource data by data store for given list of cm handles using dmi client.
- *
- * @param dataStoreName data store name
- * @param cmHandleIds list of cm handles
- * @param resourceId resource identifier
- * @param optionsParamInQuery options query
- * @param topicParamInQuery topic name for (triggering) async responses
- * @param requestId requestId for async responses
- * @return {@code ResponseEntity} response entity
- */
- public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
- final List<String> cmHandleIds,
- final String resourceId,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId) {
- final Collection<YangModelCmHandle> yangModelCmHandles
- = inventoryPersistence.getYangModelCmHandles(cmHandleIds);
- final Map<String, Map<String, Map<String, String>>> dmiServiceNameCmHandlePropertiesMap =
- DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
-
- buildBulkResourceDataRequestAndSend(dataStoreName, resourceId, optionsParamInQuery,
- topicParamInQuery, requestId, dmiServiceNameCmHandlePropertiesMap);
- return new ResponseEntity<>(HttpStatus.ACCEPTED);
- }
-
- /**
* This method fetches all the resource data from operational data store for given cm handle
* identifier using dmi client.
*
- * @param dataStoreName data store name
- * @param cmHandleId network resource identifier
- * @param requestId requestId for async responses
+ * @param dataStoreName data store name
+ * @param cmHandleId network resource identifier
+ * @param requestId requestId for async responses
* @return {@code ResponseEntity} response entity
*/
public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
@@ -130,12 +105,37 @@ public class DmiDataOperations extends DmiOperations {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
yangModelCmHandle);
- final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/", null,
- null, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
+ final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/",
+ null, null,
+ yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
- READ);
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
+ }
+
+ /**
+ * This method requests the resource data by data store for given list of cm handles using dmi client.
+ * The data wil be returned as message on the topic specified.
+ *
+ * @param topicParamInQuery topic name for (triggering) async responses
+ * @param resourceDataBatchRequest batch request for resource data
+ * @param requestId requestId for as a response
+ */
+ public void requestResourceDataFromDmi(final String topicParamInQuery,
+ final ResourceDataBatchRequest resourceDataBatchRequest,
+ final String requestId) {
+
+ final Set<String> cmHandlesIds
+ = getDistinctCmHandleIdsFromBatchRequest(resourceDataBatchRequest);
+
+ final Collection<YangModelCmHandle> yangModelCmHandles
+ = getYangModelCmHandlesInReadyState(cmHandlesIds);
+
+ final Map<String, List<DmiBatchOperation>> operationsOutPerDmiServiceName
+ = ResourceDataBatchRequestUtils.processPerOperationInBatchRequest(resourceDataBatchRequest,
+ yangModelCmHandles);
+
+ buildBatchRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName);
}
/**
@@ -143,36 +143,39 @@ public class DmiDataOperations extends DmiOperations {
* identifier on given resource using dmi client.
*
* @param cmHandleId network resource identifier
- * @param resourceId resource identifier
- * @param operation operation enum
- * @param requestData the request data
- * @param dataType data type
+ * @param resourceId resource identifier
+ * @param operationType operation enum
+ * @param requestData the request data
+ * @param dataType data type
* @return {@code ResponseEntity} response entity
*/
public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleId,
final String resourceId,
- final OperationEnum operation,
+ final OperationType operationType,
final String requestData,
final String dataType) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
- final String jsonRequestBody = getDmiRequestBody(operation, null, requestData, dataType,
+ final String jsonRequestBody = getDmiRequestBody(operationType, null, requestData, dataType,
yangModelCmHandle);
final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId,
null, null,
yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operation);
+ return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operationType);
}
private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
return inventoryPersistence.getYangModelCmHandle(cmHandleId);
}
- private String getDmiRequestBody(final OperationEnum operation, final String requestId, final String requestData,
- final String dataType, final YangModelCmHandle yangModelCmHandle) {
+ private String getDmiRequestBody(final OperationType operationType,
+ final String requestId,
+ final String requestData,
+ final String dataType,
+ final YangModelCmHandle yangModelCmHandle) {
final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
- .operation(operation)
+ .operationType(operationType)
.requestId(requestId)
.data(requestData)
.dataType(dataType)
@@ -181,17 +184,6 @@ public class DmiDataOperations extends DmiOperations {
return jsonObjectMapper.asJsonString(dmiRequestBody);
}
- private String getDmiBulkRequestBody(final OperationEnum operation,
- final String requestId,
- final String requestData) {
- final DmiRequestBody dmiBulkRequestBody = DmiRequestBody.builder()
- .operation(operation)
- .requestId(requestId)
- .data(requestData)
- .build();
- return jsonObjectMapper.asJsonString(dmiBulkRequestBody);
- }
-
private String getDmiRequestUrl(final String dataStoreName,
final String cmHandleId,
final String resourceId,
@@ -204,15 +196,13 @@ public class DmiDataOperations extends DmiOperations {
cmHandleId));
}
- private String getDmiServiceBulkRequestUrl(final String dataStoreName,
- final String resourceId,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String dmiServiceName) {
- return dmiServiceUrlBuilder.getBulkRequestUrl(
- dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
- topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
- NO_CM_HANDLE_ID));
+ private String getDmiServiceBatchRequestUrl(final String dmiServiceName,
+ final String topicParamInQuery,
+ final String requestId) {
+ final MultiValueMap<String, String> batchRequestQueryParams = dmiServiceUrlBuilder
+ .getBatchRequestQueryParams(topicParamInQuery, requestId);
+ return dmiServiceUrlBuilder.getBatchRequestUrl(batchRequestQueryParams,
+ dmiServiceUrlBuilder.populateBatchUriVariables(dmiServiceName));
}
private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle,
@@ -224,31 +214,41 @@ public class DmiDataOperations extends DmiOperations {
}
}
- private void buildBulkResourceDataRequestAndSend(final String dataStoreName,
- final String resourceId,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final Map<String, Map<String, Map<String, String>>>
- dmiServiceNameCmHandlePropertiesMap) {
- dmiServiceNameCmHandlePropertiesMap.entrySet().parallelStream().forEach(
- dmiServiceNameCmHandlePropertiesEntry -> {
- final String dmiBulkResourceDataUrl = getDmiServiceBulkRequestUrl(dataStoreName, resourceId,
- optionsParamInQuery, topicParamInQuery, dmiServiceNameCmHandlePropertiesEntry.getKey());
- final String jsonRequestBodyAsJsonString =
- jsonObjectMapper.asJsonString(dmiServiceNameCmHandlePropertiesEntry.getValue());
- final String jsonRequestBody
- = getDmiBulkRequestBody(READ, requestId, jsonRequestBodyAsJsonString);
- sendDmiResourceDataRequestToDmiService(dmiBulkResourceDataUrl, jsonRequestBody);
- });
+ private static Set<String> getDistinctCmHandleIdsFromBatchRequest(final ResourceDataBatchRequest
+ resourceDataBatchRequest) {
+ return resourceDataBatchRequest.getBatchOperationDefinitions().stream()
+ .flatMap(batchOperationDefinition ->
+ batchOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet());
+ }
+
+ private Collection<YangModelCmHandle> getYangModelCmHandlesInReadyState(final Set<String> requestedCmHandleIds) {
+ // TODO Need to publish an error response to client given topic.
+ // Code should be implemented into https://jira.onap.org/browse/CPS-1614 (
+ // NCMP : Error handling for non-ready cm handle state)
+ return inventoryPersistence.getYangModelCmHandles(requestedCmHandleIds).stream()
+ .filter(yangModelCmHandle -> yangModelCmHandle.getCompositeState().getCmHandleState()
+ == CmHandleState.READY).collect(Collectors.toList());
+ }
+
+ private void buildBatchRequestUrlAndSendToDmiService(final String topicParamInQuery,
+ final String requestId,
+ final Map<String, List<DmiBatchOperation>>
+ groupsOutPerDmiServiceName) {
+
+ groupsOutPerDmiServiceName.entrySet().forEach(groupsOutPerDmiServiceNameEntry -> {
+ final String dmiServiceName = groupsOutPerDmiServiceNameEntry.getKey();
+ final List<DmiBatchOperation> dmiBatchRequestBodies = groupsOutPerDmiServiceNameEntry.getValue();
+ final String dmiBatchResourceDataUrl = getDmiServiceBatchRequestUrl(dmiServiceName, topicParamInQuery,
+ requestId);
+ sendBatchRequestToDmiService(dmiBatchResourceDataUrl, dmiBatchRequestBodies);
+ });
}
- private void sendDmiResourceDataRequestToDmiService(final String dmiBulkResourceDataUrl,
- final String dmiResourceDataRequestAsJsonString) {
- TaskExecutor.executeTask(() ->
- dmiRestClient.postOperationWithJsonData(dmiBulkResourceDataUrl,
- dmiResourceDataRequestAsJsonString, READ),
- DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
+ private void sendBatchRequestToDmiService(final String batchResourceDataUrl,
+ final List<DmiBatchOperation> dmiBatchRequestBodies) {
+ final String batchRequestBodiesAsJsonString = jsonObjectMapper.asJsonString(dmiBatchRequestBodies);
+ TaskExecutor.executeTask(() -> dmiRestClient.postOperationWithJsonData(batchResourceDataUrl,
+ batchRequestBodiesAsJsonString, READ), DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
.whenCompleteAsync(this::handleTaskCompletion);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
index 392e9c1a24..1bbd725646 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
@@ -66,8 +66,7 @@ public class DmiModelOperations extends DmiOperations {
* @return module references
*/
public List<ModuleReference> getModuleReferences(final YangModelCmHandle yangModelCmHandle) {
- final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
- .build();
+ final DmiRequestBody dmiRequestBody = DmiRequestBody.builder().build();
dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
final ResponseEntity<Object> dmiFetchModulesResponseEntity = getResourceFromDmiWithJsonData(
yangModelCmHandle.resolveDmiServiceName(MODEL),
@@ -109,7 +108,7 @@ public class DmiModelOperations extends DmiOperations {
final String resourceName) {
final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
- OperationEnum.READ);
+ OperationType.READ);
}
private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences,
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
index 3aa6366155..6613d3c87c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ package org.onap.cps.ncmp.api.impl.operations;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -32,9 +33,11 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Builder
+@JsonPropertyOrder({"operation", "dataType", "data", "cmHandleProperties", "requestId"})
public class DmiRequestBody {
- private OperationEnum operation;
+ @JsonProperty("operation")
+ private OperationType operationType;
private String dataType;
private String data;
@JsonProperty("cmHandleProperties")
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java
new file mode 100644
index 0000000000..fa00d1a15e
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException;
+
+@Getter
+public enum OperationType {
+
+ READ("read"),
+ CREATE("create"),
+ UPDATE("update"),
+ PATCH("patch"),
+ DELETE("delete");
+
+ private final String operationName;
+
+ OperationType(final String operationName) {
+ this.operationName = operationName;
+ }
+
+ @Override
+ @JsonValue
+ public String toString() {
+ return String.valueOf(operationName);
+ }
+
+ private static final Map<String, OperationType> operationNameToOperationEnum = new HashMap<>();
+
+ static {
+ Arrays.stream(OperationType.values()).forEach(
+ operationType -> operationNameToOperationEnum.put(operationType.getOperationName(), operationType));
+ }
+
+ /**
+ * From operation name get operation enum type.
+ *
+ * @param operationName the operation name
+ * @return the operation enum type
+ */
+ public static OperationType fromOperationName(final String operationName) {
+ final OperationType operationType = operationNameToOperationEnum.get(operationName);
+ if (null == operationType) {
+ throw new InvalidOperationException(operationName + " is an invalid operation name");
+ }
+ return operationType;
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
index bba8f48fbd..5c6fa9f0b0 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
@@ -53,15 +53,17 @@ public class DmiServiceUrlBuilder {
}
/**
- * This method creates the dmi service url for bulk request.
+ * This method builds batch request url.
*
- * @param queryParams query param map as key,value pair
- * @param uriVariables uri param map as key (placeholder),value pair
- * @return {@code String} dmi service url as string
+ * @param batchRequestQueryParams query param map as key, value pair
+ * @param batchRequestUriVariables uri param map as key (placeholder), value pair
+ * @return {@code String} batch request url as string
*/
- public String getBulkRequestUrl(final MultiValueMap<String, String> queryParams,
- final Map<String, Object> uriVariables) {
- return getUriComponentsBuilder(getBulkResourceDataBasePathUriBuilder(), queryParams, uriVariables)
+ public String getBatchRequestUrl(final MultiValueMap<String, String> batchRequestQueryParams,
+ final Map<String, Object> batchRequestUriVariables) {
+ return getBatchResourceDataBasePathUriBuilder()
+ .queryParams(batchRequestQueryParams)
+ .uriVariables(batchRequestUriVariables)
.buildAndExpand().toUriString();
}
@@ -84,12 +86,12 @@ public class DmiServiceUrlBuilder {
*
* @return {@code UriComponentsBuilder} dmi service url builder object
*/
- public UriComponentsBuilder getBulkResourceDataBasePathUriBuilder() {
+ public UriComponentsBuilder getBatchResourceDataBasePathUriBuilder() {
return UriComponentsBuilder.newInstance()
.path("{dmiServiceName}")
.pathSegment("{dmiBasePath}")
.pathSegment("v1")
- .pathSegment("batch");
+ .pathSegment("data");
}
/**
@@ -114,6 +116,20 @@ public class DmiServiceUrlBuilder {
}
/**
+ * This method populates uri variables for batch request.
+ *
+ * @param dmiServiceName dmi service name
+ * @return {@code Map<String, Object>} uri variables as map
+ */
+ public Map<String, Object> populateBatchUriVariables(final String dmiServiceName) {
+ final Map<String, Object> uriVariables = new HashMap<>();
+ final String dmiBasePath = dmiProperties.getDmiBasePath();
+ uriVariables.put("dmiServiceName", dmiServiceName);
+ uriVariables.put("dmiBasePath", dmiBasePath);
+ return uriVariables;
+ }
+
+ /**
* This method is used to populate map from query params.
*
* @param resourceId unique id of response for valid topic
@@ -134,6 +150,21 @@ public class DmiServiceUrlBuilder {
return queryParams;
}
+ /**
+ * This method is used to populate map from query params for batch request.
+ *
+ * @param topicParamInQuery topic into url param
+ * @param requestId unique id of response for valid topic
+ * @return all valid query params as map
+ */
+ public MultiValueMap<String, String> getBatchRequestQueryParams(final String topicParamInQuery,
+ final String requestId) {
+ final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
+ getQueryParamConsumer().accept("topic", topicParamInQuery, queryParams);
+ getQueryParamConsumer().accept("requestId", requestId, queryParams);
+ return queryParams;
+ }
+
private TriConsumer<String, String, MultiValueMap<String, String>> getQueryParamConsumer() {
return (paramName, paramValue, paramMap) -> {
if (Strings.isNotEmpty(paramValue)) {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java
new file mode 100644
index 0000000000..e4c9bfb39b
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java
@@ -0,0 +1,126 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.operations.CmHandle;
+import org.onap.cps.ncmp.api.impl.operations.DmiBatchOperation;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.models.BatchOperationDefinition;
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest;
+
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class ResourceDataBatchRequestUtils {
+
+ private static final String UNKNOWN_SERVICE_NAME = null;
+
+ /**
+ * Create a list of DMI batch operation per DMI service (name).
+ *
+ * @param resourceDataBatchRequestIn incoming batch request details for resource data
+ * @param yangModelCmHandles involved cm handles represented as YangModelCmHandle (incl. metadata)
+ *
+ * @return {@code Map<String, List<DmiBatchOperation>>} Create a list of DMI batch operation per DMI service (name).
+ */
+ public static Map<String, List<DmiBatchOperation>> processPerOperationInBatchRequest(
+ final ResourceDataBatchRequest resourceDataBatchRequestIn,
+ final Collection<YangModelCmHandle> yangModelCmHandles) {
+
+ final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName =
+ DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
+
+ final Map<String, String> dmiServiceNamesPerCmHandleId =
+ getDmiServiceNamesPerCmHandleId(dmiPropertiesPerCmHandleIdPerServiceName);
+
+ final Map<String, List<DmiBatchOperation>> dmiBatchOperationsOutPerDmiServiceName = new HashMap<>();
+
+ for (final BatchOperationDefinition batchOperationDefinitionIn :
+ resourceDataBatchRequestIn.getBatchOperationDefinitions()) {
+ for (final String cmHandleId : batchOperationDefinitionIn.getCmHandleIds()) {
+ final String dmiServiceName = dmiServiceNamesPerCmHandleId.get(cmHandleId);
+ final Map<String, String> cmHandleIdProperties
+ = dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).get(cmHandleId);
+ if (cmHandleIdProperties == null) {
+ publishErrorMessageToClientTopic(cmHandleId);
+ } else {
+ final DmiBatchOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName,
+ batchOperationDefinitionIn, dmiBatchOperationsOutPerDmiServiceName);
+ final CmHandle cmHandle = CmHandle.buildCmHandleWithProperties(cmHandleId, cmHandleIdProperties);
+ dmiBatchOperationOut.getCmHandles().add(cmHandle);
+ }
+ }
+ }
+ return dmiBatchOperationsOutPerDmiServiceName;
+ }
+
+ private static void publishErrorMessageToClientTopic(final String requestedCmHandleId) {
+ log.warn("cm handle {} not found", requestedCmHandleId);
+ // TODO Need to publish an error response to client given topic.
+ // Code should be implemented into https://jira.onap.org/browse/CPS-1583 (
+ // NCMP : Handle non-existing cm handles)
+ }
+
+ private static Map<String, String> getDmiServiceNamesPerCmHandleId(
+ final Map<String, Map<String, Map<String, String>>> dmiDmiPropertiesPerCmHandleIdPerServiceName) {
+ final Map<String, String> dmiServiceNamesPerCmHandleId = new HashMap<>();
+ for (final Map.Entry<String, Map<String, Map<String, String>>> dmiDmiPropertiesEntry
+ : dmiDmiPropertiesPerCmHandleIdPerServiceName.entrySet()) {
+ final String dmiServiceName = dmiDmiPropertiesEntry.getKey();
+ final Set<String> cmHandleIds = dmiDmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).keySet();
+ for (final String cmHandleId : cmHandleIds) {
+ dmiServiceNamesPerCmHandleId.put(cmHandleId, dmiServiceName);
+ }
+ }
+ dmiDmiPropertiesPerCmHandleIdPerServiceName.put(UNKNOWN_SERVICE_NAME, Collections.emptyMap());
+ return dmiServiceNamesPerCmHandleId;
+ }
+
+ private static DmiBatchOperation getOrAddDmiBatchOperation(final String dmiServiceName,
+ final BatchOperationDefinition
+ batchOperationDefinitionIn,
+ final Map<String, List<DmiBatchOperation>>
+ dmiBatchOperationsOutPerDmiServiceName) {
+ dmiBatchOperationsOutPerDmiServiceName
+ .computeIfAbsent(dmiServiceName, dmiServiceNameAsKey -> new ArrayList<>());
+ final List<DmiBatchOperation> dmiBatchOperationsOut
+ = dmiBatchOperationsOutPerDmiServiceName.get(dmiServiceName);
+ final boolean isNewOperation = dmiBatchOperationsOut.isEmpty()
+ || !dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1).getOperationId()
+ .equals(batchOperationDefinitionIn.getOperationId());
+ if (isNewOperation) {
+ final DmiBatchOperation newDmiBatchOperationOut =
+ DmiBatchOperation.buildDmiBatchRequestBodyWithoutCmHandles(batchOperationDefinitionIn);
+ dmiBatchOperationsOut.add(newDmiBatchOperationOut);
+ return newDmiBatchOperationOut;
+ }
+ return dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1);
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java
new file mode 100644
index 0000000000..04075b3b7c
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.Valid;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BatchOperationDefinition {
+
+ private String operation;
+ private String operationId;
+ private String datastore;
+ private String options;
+ private String resourceIdentifier;
+
+ @JsonProperty("targetIds")
+ @Valid
+ private List<String> cmHandleIds = new ArrayList();
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java
new file mode 100644
index 0000000000..7af107c37a
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Collections;
+import java.util.List;
+import javax.validation.Valid;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ResourceDataBatchRequest {
+
+ @JsonProperty("operations")
+ @Valid
+ private List<BatchOperationDefinition> batchOperationDefinitions = Collections.emptyList();
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
index 3d8e9cb2e8..79f7e50e76 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
@@ -33,11 +33,13 @@ import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
+import org.onap.cps.ncmp.api.models.BatchOperationDefinition
import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
import org.onap.cps.ncmp.api.models.ConditionApiProperties
import org.onap.cps.ncmp.api.models.DmiPluginRegistration
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest
import org.onap.cps.spi.exceptions.CpsException
import org.onap.cps.spi.model.ConditionProperties
import spock.lang.Shared
@@ -54,8 +56,8 @@ import spock.lang.Specification
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
class NetworkCmProxyDataServiceImplSpec extends Specification {
@@ -133,21 +135,13 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
response == '{dmi-response}'
}
- def 'Get bulk resource data for #datastoreName from DMI.'() {
+ def 'Get batch resource data for #datastoreName from DMI.'() {
given: 'cpsDataService returns valid data node'
- mockDataNode()
- and: 'DMI returns valid response and data'
- mockDmiDataOperations.getResourceDataFromDmi(datastoreName, ['testCmHandle'],
- 'testResourceId', OPTIONS_PARAM,'some topic','requestId') >>
- new ResponseEntity<>('{dmi-bulk-response}', HttpStatus.OK)
+ def resourceDataBatchRequest = getResourceDataBatchRequest(datastoreName)
when: 'get batch resource data is called'
- def response = objectUnderTest.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'],
- 'testResourceId',
- OPTIONS_PARAM,
- 'some topic',
- 'requestId')
- then: 'get bulk resource data returns expected response'
- response == '{dmi-bulk-response}'
+ objectUnderTest.requestResourceDataForCmHandleBatch('some topic', resourceDataBatchRequest, 'requestId')
+ then: 'get batch resource data returns expected response'
+ 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', resourceDataBatchRequest, 'requestId')
where: 'the following data stores are used'
datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
}
@@ -373,4 +367,22 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
}
+
+ def getResourceDataBatchRequest(datastore) {
+ def resourceDataBatchRequest = new ResourceDataBatchRequest()
+ def batchOperationDefinitions = new ArrayList()
+ batchOperationDefinitions.add(getBatchOperationDefinition(datastore))
+ resourceDataBatchRequest.setBatchOperationDefinitions(batchOperationDefinitions)
+ }
+
+ def getBatchOperationDefinition(datastore) {
+ def batchOperationDefinition = new BatchOperationDefinition()
+ batchOperationDefinition.setOperation("read")
+ batchOperationDefinition.setOperationId("operational-12")
+ batchOperationDefinition.setDatastore(datastore)
+ def targetIds = new ArrayList()
+ targetIds.add("some-cm-handle")
+ batchOperationDefinition.setCmHandleIds(targetIds)
+ return batchOperationDefinition
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy
new file mode 100644
index 0000000000..65c43a011d
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy
@@ -0,0 +1,104 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.apache.commons.lang3.SerializationUtils
+import org.apache.kafka.clients.consumer.ConsumerRecord
+import org.apache.kafka.clients.consumer.KafkaConsumer
+import org.apache.kafka.common.header.internals.RecordHeader
+import org.onap.cps.ncmp.api.impl.events.EventsPublisher
+import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
+import org.onap.cps.ncmp.events.async.BatchDataResponseEventV1
+import org.onap.cps.ncmp.utils.TestUtils
+import org.onap.cps.utils.JsonObjectMapper
+import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.kafka.listener.adapter.RecordFilterStrategy
+import org.springframework.test.annotation.DirtiesContext
+import org.testcontainers.spock.Testcontainers
+
+import java.time.Duration
+
+@SpringBootTest(classes = [EventsPublisher, NcmpAsyncBatchEventConsumer, BatchRecordFilterStrategy,JsonObjectMapper,
+ ObjectMapper])
+@Testcontainers
+@DirtiesContext
+class NcmpAsyncBatchEventConsumerSpec extends MessagingBaseSpec {
+
+ @SpringBean
+ EventsPublisher asyncBatchEventPublisher = new EventsPublisher<BatchDataResponseEventV1>(kafkaTemplate)
+
+ @SpringBean
+ NcmpAsyncBatchEventConsumer asyncBatchEventConsumer = new NcmpAsyncBatchEventConsumer(asyncBatchEventPublisher)
+
+ @Autowired
+ JsonObjectMapper jsonObjectMapper
+
+ @Autowired
+ RecordFilterStrategy<Object, Object> recordFilterStrategy
+
+ def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('test'))
+ def static clientTopic = 'client-topic'
+ def static batchEventType = 'org.onap.cps.ncmp.events.async.BatchDataResponseEventV1'
+
+ def 'Consume and publish event to client specified topic'() {
+ given: 'consumer subscribing to client topic'
+ kafkaConsumer.subscribe([clientTopic])
+ and: 'consumer record for batch event'
+ def consumerRecordIn = createConsumerRecord(batchEventType)
+ when: 'the batch event is consumed and published to client specified topic'
+ asyncBatchEventConsumer.consumeAndPublish(consumerRecordIn)
+ and: 'the client specified topic is polled'
+ def consumerRecordOut = kafkaConsumer.poll(Duration.ofMillis(1500))[0]
+ then: 'verifying consumed event operationID is same as published event operationID'
+ def operationIdIn = consumerRecordIn.value.event.batchResponses[0].operationId
+ def operationIdOut = jsonObjectMapper.convertJsonString((String)consumerRecordOut.value(), BatchDataResponseEventV1.class).event.batchResponses[0].operationId
+ assert operationIdIn == operationIdOut
+ }
+
+ def 'Filter an event with type #eventType'() {
+ given: 'consumer record for event with type #eventType'
+ def consumerRecord = createConsumerRecord(eventType)
+ when: 'while consuming the topic ncmp-async-m2m it executes the filter strategy'
+ def result = recordFilterStrategy.filter(consumerRecord)
+ then: 'the event is #description'
+ assert result == expectedResult
+ where: 'filter the event based on the eventType #eventType'
+ description | eventType || expectedResult
+ 'not filtered(the consumer will see the event)' | batchEventType || false
+ 'filtered(the consumer will not see the event)' | 'wrongType' || true
+ }
+
+ def createConsumerRecord(eventTypeAsString) {
+ def jsonData = TestUtils.getResourceFileContent('batchDataEvent.json')
+ def testEventSent = jsonObjectMapper.convertJsonString(jsonData, BatchDataResponseEventV1.class)
+ def eventTarget = SerializationUtils.serialize(clientTopic)
+ def eventType = SerializationUtils.serialize(eventTypeAsString)
+ def eventId = SerializationUtils.serialize('12345')
+ def consumerRecord = new ConsumerRecord<String, Object>(clientTopic, 1, 1L, '123', testEventSent)
+ consumerRecord.headers().add(new RecordHeader('eventId', eventId))
+ consumerRecord.headers().add(new RecordHeader('eventTarget', eventTarget))
+ consumerRecord.headers().add(new RecordHeader('eventType', eventType))
+ return consumerRecord
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
index b38ca10f7b..6b0355eee8 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
@@ -34,9 +34,9 @@ import org.springframework.web.client.HttpServerErrorException
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient])
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy
index f660be7103..e449d65ac2 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy
@@ -54,7 +54,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
then: 'state is saved using inventory persistence'
expectedCallsToInventoryPersistence * mockInventoryPersistence.saveCmHandleState(cmHandleId, _)
and: 'event service is called to publish event'
- expectedCallsToEventService * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
+ expectedCallsToEventService * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
where: 'state change parameters are provided'
stateChange | fromCmHandleState | toCmHandleState || expectedCallsToInventoryPersistence | expectedCallsToEventService
'ADVISED to READY' | ADVISED | READY || 1 | 1
@@ -73,7 +73,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
then: 'state is saved using inventory persistence'
1 * mockInventoryPersistence.saveCmHandle(yangModelCmHandle)
and: 'event service is called to publish event'
- 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
+ 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
}
def 'Update and Publish Events on State Change from LOCKED to ADVISED'() {
@@ -90,7 +90,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
}
}
and: 'event service is called to publish event'
- 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
+ 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
}
def 'Update and Publish Events on State Change to READY'() {
@@ -111,7 +111,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
}
}
and: 'event service is called to publish event'
- 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
+ 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
}
def 'Update cmHandle state to "DELETING"' (){
@@ -125,7 +125,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
and: 'method to persist cm handle state is called once'
1 * mockInventoryPersistence.saveCmHandleState(yangModelCmHandle.getId(), yangModelCmHandle.getCompositeState())
and: 'the method to publish Lcm event is called once'
- 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
+ 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
}
def 'Update cmHandle state to "DELETED"' (){
@@ -137,7 +137,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
then: 'the cm handle state is as expected'
yangModelCmHandle.getCompositeState().getCmHandleState() == DELETED
and: 'the method to publish Lcm event is called once'
- 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
+ 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
}
def 'No state change and no event to be published'() {
@@ -167,7 +167,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
}
}
and: 'event service is called to publish event'
- 2 * mockLcmEventsService.publishLcmEvent(_, _)
+ 2 * mockLcmEventsService.publishLcmEvent(_, _, _)
}
@@ -183,7 +183,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
}
}
and: 'event service is called to publish event'
- 2 * mockLcmEventsService.publishLcmEvent(_, _)
+ 2 * mockLcmEventsService.publishLcmEvent(_, _, _)
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorSpec.groovy
index f4adfc587c..6d7d6250f1 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorSpec.groovy
@@ -20,10 +20,11 @@
package org.onap.cps.ncmp.api.impl.events.lcm
+import org.mapstruct.factory.Mappers
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.ncmp.cmhandle.event.lcm.Values
+import org.onap.cps.ncmp.events.lcm.v1.Values
import spock.lang.Specification
import static org.onap.cps.ncmp.api.inventory.CmHandleState.ADVISED
@@ -32,7 +33,9 @@ import static org.onap.cps.ncmp.api.inventory.CmHandleState.READY
class LcmEventsCreatorSpec extends Specification {
- def objectUnderTest = new LcmEventsCreator()
+ LcmEventHeaderMapper lcmEventsHeaderMapper = Mappers.getMapper(LcmEventHeaderMapper)
+
+ def objectUnderTest = new LcmEventsCreator(lcmEventsHeaderMapper)
def cmHandleId = 'test-cm-handle'
def 'Map the LcmEvent for #operation'() {
@@ -159,4 +162,15 @@ class LcmEventsCreatorSpec extends Specification {
'null to null' | null | null
}
+
+ def 'Map the LcmEventHeader'() {
+ given: 'NCMP cm handle details with current and old details'
+ def existingNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: new CompositeState(cmHandleState: ADVISED))
+ def targetNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: new CompositeState(cmHandleState: READY))
+ when: 'the event header is populated'
+ def result = objectUnderTest.populateLcmEventHeader(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle)
+ then: 'the header has fields populated'
+ assert result.eventCorrelationId == cmHandleId
+ assert result.eventId != null
+ }
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy
index 7c9464dccb..93741261f6 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy
@@ -24,14 +24,15 @@ import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.kafka.clients.consumer.KafkaConsumer
import org.onap.cps.ncmp.api.impl.events.EventsPublisher
import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
+import org.onap.cps.ncmp.events.lcm.v1.Event
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
-import org.onap.ncmp.cmhandle.event.lcm.Event
-import org.onap.ncmp.cmhandle.event.lcm.LcmEvent
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext
+import org.springframework.util.SerializationUtils
import org.testcontainers.spock.Testcontainers
import java.time.Duration
@@ -55,19 +56,35 @@ class LcmEventsPublisherSpec extends MessagingBaseSpec {
def 'Produce and Consume Lcm Event'() {
given: 'event key and event data'
def eventKey = 'lcm'
+ def eventId = 'test-uuid'
+ def eventCorrelationId = 'cmhandle-test'
+ def eventSource = 'org.onap.ncmp'
+ def eventTime = '2022-12-31T20:30:40.000+0000'
+ def eventType = 'org.onap.ncmp.cmhandle.lcm.event'
+ def eventSchema = 'org.onap.ncmp.cmhandle.lcm.event'
+ def eventSchemaVersion = 'v1'
def eventData = new LcmEvent(
- eventId: 'test-uuid',
- eventCorrelationId: 'cmhandle-as-correlationid',
- eventSource: 'org.onap.ncmp',
- eventTime: '2022-12-31T20:30:40.000+0000',
- eventType: 'org.onap.ncmp.cmhandle.lcm.event',
- eventSchema: 'org.onap.ncmp.cmhandle.lcm.event',
- eventSchemaVersion: 'v1',
+ eventId: eventId,
+ eventCorrelationId: eventCorrelationId,
+ eventSource: eventSource,
+ eventTime: eventTime,
+ eventType: eventType,
+ eventSchema: eventSchema,
+ eventSchemaVersion: eventSchemaVersion,
event: new Event(cmHandleId: 'cmhandle-test'))
+ and: 'we have a event header'
+ def eventHeader = [
+ eventId : eventId,
+ eventCorrelationId: eventCorrelationId,
+ eventSource : eventSource,
+ eventTime : eventTime,
+ eventType : eventType,
+ eventSchema : eventSchema,
+ eventSchemaVersion: eventSchemaVersion]
and: 'consumer has a subscription'
kafkaConsumer.subscribe([testTopic] as List<String>)
when: 'an event is published'
- lcmEventsPublisher.publishEvent(testTopic, eventKey, eventData)
+ lcmEventsPublisher.publishEvent(testTopic, eventKey, eventHeader, eventData)
and: 'topic is polled'
def records = kafkaConsumer.poll(Duration.ofMillis(1500))
then: 'poll returns one record'
@@ -79,5 +96,8 @@ class LcmEventsPublisherSpec extends MessagingBaseSpec {
def expectedJsonString = TestUtils.getResourceFileContent('expectedLcmEvent.json')
def expectedLcmEvent = jsonObjectMapper.convertJsonString(expectedJsonString, LcmEvent.class)
assert expectedLcmEvent == jsonObjectMapper.convertJsonString(record.value, LcmEvent.class)
+ and: 'record header matches the expected parameters'
+ assert SerializationUtils.deserialize(record.headers().lastHeader('eventId').value()) == eventId
+ assert SerializationUtils.deserialize(record.headers().lastHeader('eventCorrelationId').value()) == eventCorrelationId
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsServiceSpec.groovy
index 65f4d50c68..2d3f8ac516 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsServiceSpec.groovy
@@ -21,26 +21,42 @@
package org.onap.cps.ncmp.api.impl.events.lcm
import org.onap.cps.ncmp.api.impl.events.EventsPublisher
-import org.onap.ncmp.cmhandle.event.lcm.LcmEvent
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
+import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader
+import org.onap.cps.utils.JsonObjectMapper
import org.springframework.kafka.KafkaException
import spock.lang.Specification
class LcmEventsServiceSpec extends Specification {
def mockLcmEventsPublisher = Mock(EventsPublisher)
+ def mockJsonObjectMapper = Mock(JsonObjectMapper)
- def objectUnderTest = new LcmEventsService(mockLcmEventsPublisher)
+ def objectUnderTest = new LcmEventsService(mockLcmEventsPublisher, mockJsonObjectMapper)
def 'Create and Publish lcm event where events are #scenario'() {
given: 'a cm handle id and Lcm Event'
def cmHandleId = 'test-cm-handle-id'
- def lcmEvent = new LcmEvent(eventId: UUID.randomUUID().toString(), eventCorrelationId: cmHandleId)
+ def eventId = UUID.randomUUID().toString()
+ def lcmEvent = new LcmEvent(eventId: eventId, eventCorrelationId: cmHandleId)
+ and: 'we also have a lcm event header'
+ def lcmEventHeader = new LcmEventHeader(eventId: eventId, eventCorrelationId: cmHandleId)
and: 'notificationsEnabled is #notificationsEnabled and it will be true as default'
objectUnderTest.notificationsEnabled = notificationsEnabled
+ and: 'lcm event header is transformed to headers map'
+ mockJsonObjectMapper.convertToValueType(lcmEventHeader, Map.class) >> ['eventId': eventId, 'eventCorrelationId': cmHandleId]
when: 'service is called to publish lcm event'
- objectUnderTest.publishLcmEvent('test-cm-handle-id', lcmEvent)
+ objectUnderTest.publishLcmEvent('test-cm-handle-id', lcmEvent, lcmEventHeader)
then: 'publisher is called #expectedTimesMethodCalled times'
- expectedTimesMethodCalled * mockLcmEventsPublisher.publishEvent(_, cmHandleId, lcmEvent)
+ expectedTimesMethodCalled * mockLcmEventsPublisher.publishEvent(_, cmHandleId, _, lcmEvent) >> {
+ args -> {
+ def eventHeaders = (args[2] as Map<String,Object>)
+ assert eventHeaders.containsKey('eventId')
+ assert eventHeaders.containsKey('eventCorrelationId')
+ assert eventHeaders.get('eventId') == eventId
+ assert eventHeaders.get('eventCorrelationId') == cmHandleId
+ }
+ }
where: 'the following values are used'
scenario | notificationsEnabled || expectedTimesMethodCalled
'enabled' | true || 1
@@ -50,12 +66,14 @@ class LcmEventsServiceSpec extends Specification {
def 'Unable to send message'(){
given: 'a cm handle id and Lcm Event and notification enabled'
def cmHandleId = 'test-cm-handle-id'
- def lcmEvent = new LcmEvent(eventId: UUID.randomUUID().toString(), eventCorrelationId: cmHandleId)
+ def eventId = UUID.randomUUID().toString()
+ def lcmEvent = new LcmEvent(eventId: eventId, eventCorrelationId: cmHandleId)
+ def lcmEventHeader = new LcmEventHeader(eventId: eventId, eventCorrelationId: cmHandleId)
objectUnderTest.notificationsEnabled = true
when: 'publisher set to throw an exception'
- mockLcmEventsPublisher.publishEvent(*_) >> { throw new KafkaException('publishing failed')}
+ mockLcmEventsPublisher.publishEvent(_, _, _, _) >> { throw new KafkaException('publishing failed')}
and: 'an event is publised'
- objectUnderTest.publishLcmEvent(cmHandleId, lcmEvent)
+ objectUnderTest.publishLcmEvent(cmHandleId, lcmEvent, lcmEventHeader)
then: 'the exception is just logged and not bubbled up'
noExceptionThrown()
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
index 5fd4fbd43f..9343666260 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
@@ -24,6 +24,8 @@ package org.onap.cps.ncmp.api.impl.operations
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest
+import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
@@ -35,9 +37,9 @@ import spock.lang.Shared
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiDataOperations])
@@ -50,8 +52,6 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def NO_REQUEST_ID = null
@Shared
def OPTIONS_PARAM = '(a=1,b=2)'
- @Shared
- def expectedBulkRequestAsJson = '{"operation": "read","data": {"fe1c1f1a070c4ce5bbfda7198592a0d3": {"neType": "RadioNode"},"b8e42eed0d9541ed8d8839e8eb86b3e0": {"neType": "RadioNode"}},"requestId": "bbb67474-f705-410a-93d1-b2844e7f45fd"}'
@SpringBean
JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
@@ -82,23 +82,26 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running' | '&options=(a=1,b=2)'
}
- def 'call get bulk resource data for #dataStore from DMI service with topic #scenario.'() {
- given: 'collection of yang model cm Handles'
+ def 'call get batch resource data from DMI service #scenario.'() {
+ given: 'collection of yang model cm Handles and resource data batch request'
mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty])
- and: 'a positive response from DMI service when it is called with the expected parameters'
+ def resourceDataBatchRequestJsonData = TestUtils.getResourceFileContent('resourceDataBatchRequest.json')
+ def resourceDataBatchRequest = spiedJsonObjectMapper.convertJsonString(resourceDataBatchRequestJsonData, ResourceDataBatchRequest.class)
+ resourceDataBatchRequest.batchOperationDefinitions[0].cmHandleIds = [cmHandleId]
+ def requestBodyAsJsonStringArg = null
+ and: 'a positive response from DMI service when it is called with valid request parameters'
def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED)
- def expectedDmiBulkResourceDataUrl = "ncmp/v1/batch/data/ds/${dataStore}?resourceIdentifier=parent/child%26options=(a=1,b=2)&topic=my-topic-name&options=(fields=schemas/schema)"
- mockDmiRestClient.postOperationWithJsonData(expectedDmiBulkResourceDataUrl, expectedBulkRequestAsJson, READ) >> responseFromDmi
- dmiServiceUrlBuilder.getBulkRequestUrl(_, _) >> expectedDmiBulkResourceDataUrl
- when: 'get resource data for bulk cm handle is invoked'
- def result = objectUnderTest.getResourceDataFromDmi( dataStore.datastoreName, [cmHandleId], resourceIdentifier,
- OPTIONS_PARAM, 'some-topic','requestId')
- then: 'the result is the response from the DMI service'
- assert result == responseFromDmi
- where: 'the following parameters are used'
- scenario | dataStore
- 'datastore operational' | PASSTHROUGH_OPERATIONAL
- 'datastore running' | PASSTHROUGH_RUNNING
+ def expectedDmiBatchResourceDataUrl = "ncmp/v1/data/topic=my-topic-name"
+ def expectedBatchRequestAsJson = '[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","cmHandleProperties":{"prop1":"val1"}}]}]'
+ mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ.operationName) >> responseFromDmi
+ dmiServiceUrlBuilder.getBatchRequestUrl(_, _) >> expectedDmiBatchResourceDataUrl
+ when: 'get resource data for batch of cm handles are invoked'
+ objectUnderTest.requestResourceDataFromDmi('my-topic-name', resourceDataBatchRequest, 'requestId')
+ then: 'wait a little to allow execution of service method by task executor (on separate thread)'
+ Thread.sleep(100)
+ then: 'validate ncmp generated dmi request body json args'
+ 1 * mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ) >> { args -> requestBodyAsJsonStringArg = args[1] }
+ assert requestBodyAsJsonStringArg == expectedBatchRequestAsJson
}
def 'call get all resource data.'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
index ed74ad3342..d1025f9d65 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
@@ -36,7 +36,7 @@ import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
import spock.lang.Shared
-import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations])
@@ -103,9 +103,9 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
then: 'the result is the response from DMI service'
assert result == []
where: 'the following DMI properties are used'
- scenario | dmiProperties || expectedAdditionalPropertiesInRequest
- 'with properties' | [yangModelCmHandleProperty] || '{"prop1":"val1"}'
- 'without properties' | [] || '{}'
+ scenario | dmiProperties || expectedAdditionalPropertiesInRequest
+ 'with properties' | [yangModelCmHandleProperty] || '{"prop1":"val1"}'
+ 'without properties' | [] || '{}'
}
def 'Retrieving yang resources.'() {
@@ -154,10 +154,10 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
then: 'the result is the response from DMI service'
assert result == [mod1:'some yang source']
where: 'the following DMI properties are used'
- scenario | dmiProperties | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest
- 'with module references and properties' | [yangModelCmHandleProperty] | newModuleReferences || '{"prop1":"val1"}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
- 'without module references' | [yangModelCmHandleProperty] | [] || '{"prop1":"val1"}' | ''
- 'without properties' | [] | newModuleReferences || '{}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
+ scenario | dmiProperties | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest
+ 'with module references and properties' | [yangModelCmHandleProperty] | newModuleReferences || '{"prop1":"val1"}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
+ 'without module references' | [yangModelCmHandleProperty] | [] || '{"prop1":"val1"}' | ''
+ 'without properties' | [] | newModuleReferences || '{}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
}
def 'Retrieving yang resources from DMI with null DMI properties.'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy
new file mode 100644
index 0000000000..e65874930b
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy
@@ -0,0 +1,80 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder
+import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest
+import org.onap.cps.ncmp.utils.TestUtils
+import org.onap.cps.utils.JsonObjectMapper
+import org.spockframework.spring.SpringBean
+import spock.lang.Specification
+
+class ResourceDataBatchRequestUtilsSpec extends Specification {
+
+ @SpringBean
+ JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
+ def 'Process per operation in batch request with #serviceName.'() {
+ given: 'batch request with 3 operations'
+ def resourceDataBatchRequestJsonData = TestUtils.getResourceFileContent('resourceDataBatchRequest.json')
+ def resourceDataBatchRequest = jsonObjectMapper.convertJsonString(resourceDataBatchRequestJsonData, ResourceDataBatchRequest.class)
+ and: '4 known cm handles: ch1-dmi1, ch2-dmi1, ch3-dmi2, ch4-dmi2'
+ def yangModelCmHandles = getYangModelCmHandles()
+ when: 'Operation in batch request is processed'
+ def operationsOutPerDmiServiceName = ResourceDataBatchRequestUtils.processPerOperationInBatchRequest(resourceDataBatchRequest, yangModelCmHandles)
+ and: 'converted to a json node'
+ def dmiBatchRequestBody = jsonObjectMapper.asJsonString(operationsOutPerDmiServiceName.get(serviceName))
+ def dmiBatchRequestBodyAsJsonNode = jsonObjectMapper.convertToJsonNode(dmiBatchRequestBody).get(operationIndex)
+ then: 'it contains the correct operation details'
+ assert dmiBatchRequestBodyAsJsonNode.get('operation').asText() == 'read'
+ assert dmiBatchRequestBodyAsJsonNode.get('operationId').asText() == expectedOperationId
+ assert dmiBatchRequestBodyAsJsonNode.get('datastore').asText() == expectedDatastore
+ and: 'the correct cm handles (just for #serviceName)'
+ assert dmiBatchRequestBodyAsJsonNode.get('cmHandles').size() == expectedCmHandleIds.size()
+ expectedCmHandleIds.each {
+ dmiBatchRequestBodyAsJsonNode.get('cmHandles').toString().contains(it)
+ }
+ where: 'the following dmi service and operations are checked'
+ serviceName | operationIndex || expectedOperationId | expectedDatastore | expectedCmHandleIds
+ 'dmi1' | 0 || 'operational-14' | 'ncmp-datastore:passthrough-operational' | ['ch6-dmi1']
+ 'dmi1' | 1 || 'running-12' | 'ncmp-datastore:passthrough-running' | ['ch1-dmi1', 'ch2-dmi1']
+ 'dmi1' | 2 || 'operational-15' | 'ncmp-datastore:passthrough-operational' | ['ch6-dmi1']
+ 'dmi2' | 0 || 'operational-14' | 'ncmp-datastore:passthrough-operational' | ['ch3-dmi2']
+ 'dmi2' | 1 || 'running-12' | 'ncmp-datastore:passthrough-running' | ['ch7-dmi2']
+ 'dmi2' | 2 || 'operational-15' | 'ncmp-datastore:passthrough-operational' | ['ch4-dmi2']
+ }
+
+ static def getYangModelCmHandles() {
+ def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
+ def readyState = new CompositeStateBuilder().withCmHandleState(CmHandleState.READY).withLastUpdatedTimeNow().build()
+ return [new YangModelCmHandle(id: 'ch1-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+ new YangModelCmHandle(id: 'ch2-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+ new YangModelCmHandle(id: 'ch6-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+ new YangModelCmHandle(id: 'ch8-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+ new YangModelCmHandle(id: 'ch3-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
+ new YangModelCmHandle(id: 'ch4-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
+ new YangModelCmHandle(id: 'ch7-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
+ ]
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy
index e1055bb217..dc471e64fa 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy
@@ -64,13 +64,13 @@ class RestQueryParametersValidatorSpec extends Specification {
and: 'the exception details contain the correct significant term '
thrown.details.contains(expectedWordInDetails)
where:
- scenario | conditionName | conditionParameters || expectedWordInDetails
- 'unknown condition name' | 'unknownCondition' | [['key':'value']] || 'conditionName'
- 'no condition name' | '' | [['key':'value']] || 'conditionName'
- 'empty properties' | 'validConditionName' | [[ : ]] || 'conditionsParameter'
- 'empty conditions' | 'validConditionName' | [[:]] || 'conditionsParameter'
- 'too many properties' | 'validConditionName' | [[key1:'value1', key2:'value2']] || 'conditionsParameter'
- 'empty key' | 'validConditionName' | [['':'wrong']] || 'conditionsParameter'
+ scenario | conditionName | conditionParameters || expectedWordInDetails
+ 'unknown condition name' | 'unknownCondition' | [['key': 'value']] || 'conditionName'
+ 'no condition name' | '' | [['key': 'value']] || 'conditionName'
+ 'empty properties' | 'validConditionName' | [[:]] || 'conditionsParameter'
+ 'empty conditions' | 'validConditionName' | [[:]] || 'conditionsParameter'
+ 'too many properties' | 'validConditionName' | [[key1: 'value1', key2: 'value2']] || 'conditionsParameter'
+ 'empty key' | 'validConditionName' | [['': 'wrong']] || 'conditionsParameter'
}
def 'CM Handle Query validation: validate module name condition properties - valid query.'() {
diff --git a/cps-ncmp-service/src/test/resources/batchDataEvent.json b/cps-ncmp-service/src/test/resources/batchDataEvent.json
new file mode 100644
index 0000000000..49eb273f58
--- /dev/null
+++ b/cps-ncmp-service/src/test/resources/batchDataEvent.json
@@ -0,0 +1,46 @@
+{
+ "event":{
+ "batch-responses":[
+ {
+ "operationId":"1",
+ "ids":[
+ "123",
+ "124"
+ ],
+ "status-code":1,
+ "status-message":"Batch operation success on the above cmhandle ids ",
+ "data":{
+ "ietf-netconf-monitoring:netconf-state":{
+ "schemas":{
+ "schema":[
+ {
+ "identifier":"ietf-tls-server",
+ "version":"2016-11-02",
+ "format":"ietf-netconf-monitoring:yang",
+ "namespace":"urn:ietf:params:xml:ns:yang:ietf-tls-server",
+ "location":[
+ "NETCONF"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "operationId":"101",
+ "ids":[
+ "456",
+ "457"
+ ],
+ "status-code":101,
+ "status-message":"cmHandle(s) do not exist",
+ "data":{
+ "error":{
+ "message":"cmHandle(s) do not exist"
+ }
+ }
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/resources/expectedLcmEvent.json b/cps-ncmp-service/src/test/resources/expectedLcmEvent.json
index 1db16ee82a..20d557dc4f 100644
--- a/cps-ncmp-service/src/test/resources/expectedLcmEvent.json
+++ b/cps-ncmp-service/src/test/resources/expectedLcmEvent.json
@@ -1,6 +1,6 @@
{
"eventId": "test-uuid",
- "eventCorrelationId": "cmhandle-as-correlationid",
+ "eventCorrelationId": "cmhandle-test",
"eventTime": "2022-12-31T20:30:40.000+0000",
"eventSource": "org.onap.ncmp",
"eventType": "org.onap.ncmp.cmhandle.lcm.event",
diff --git a/cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json b/cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json
new file mode 100644
index 0000000000..98ed39b9ae
--- /dev/null
+++ b/cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json
@@ -0,0 +1,36 @@
+{
+ "operations": [
+ {
+ "operation": "read",
+ "operationId": "operational-14",
+ "datastore": "ncmp-datastore:passthrough-operational",
+ "options": "some option",
+ "resourceIdentifier": "some resource identifier",
+ "targetIds": [
+ "ch3-dmi2",
+ "unknown-cm-handle",
+ "ch6-dmi1"
+ ]
+ },
+ {
+ "operation": "read",
+ "operationId": "running-12",
+ "datastore": "ncmp-datastore:passthrough-running",
+ "targetIds": [
+ "ch1-dmi1",
+ "ch7-dmi2",
+ "ch2-dmi1"
+ ]
+ },
+ {
+ "operation": "read",
+ "operationId": "operational-15",
+ "datastore": "ncmp-datastore:passthrough-operational",
+ "options": "some option",
+ "targetIds": [
+ "ch4-dmi2",
+ "ch6-dmi1"
+ ]
+ }
+ ]
+}
diff --git a/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json b/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json
index de8a523c0f..569343fed9 100644
--- a/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json
+++ b/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json
@@ -1,5 +1,42 @@
{
- "event": {
- "payload": "Hello world!"
+ "event":{
+ "push-change-update":{
+ "datastore-changes":{
+ "ietf-yang-patch:yang-patch":{
+ "patch-id":"34534ffd98",
+ "edit":[
+ {
+ "edit-id":"ded43434-1",
+ "operation":"replace",
+ "target":"ran-network:ran-network/NearRTRIC[@id='22']/GNBCUCPFunction[@id='cucpserver2']/NRCellCU[@id='15549']/NRCellRelation[@id='14427']",
+ "value":{
+ "attributes":[
+ {
+ "isHoAllowed":true
+ }
+ ]
+ }
+ },
+ {
+ "edit-id":"ded43434-2",
+ "operation":"create",
+ "target":"ran-network:ran-network/NearRTRIC[@id='22']/GNBCUCPFunction[@id='cucpserver1']/NRCellCU[@id='15548']/NRCellRelation[@id='14426']",
+ "value":{
+ "attributes":[
+ {
+ "isHoAllowed":false
+ }
+ ]
+ }
+ },
+ {
+ "edit-id":"ded43434-3",
+ "operation":"delete",
+ "target":"ran-network:ran-network/NearRTRIC[@id='22']/GNBCUCPFunction[@id='cucpserver1']/NRCellCU[@id='15548']/NRCellRelation[@id='14426']"
+ }
+ ]
+ }
+ }
+ }
}
} \ No newline at end of file
diff --git a/dmi-plugin-stub/files/batchResponse.json b/dmi-plugin-stub/files/batchResponse.json
index b0615df945..9181b64f21 100644
--- a/dmi-plugin-stub/files/batchResponse.json
+++ b/dmi-plugin-stub/files/batchResponse.json
@@ -2,8 +2,8 @@
"eventId": "4cb32729-85e3-44d1-aa6e-c923b9b059a5",
"eventCorrelationId": "68f15800-8ed4-4bae-9e53-27a9e03e1911",
"eventTime": "2023-03-28T14:29:23.876+0000",
- "eventType": "org.onap.cps.ncmp.event.model.BulkResponseEvent",
- "eventSchema": "urn:cps:org.onap.cps.ncmp.event.model.BulkResponseEvent",
+ "eventType": "org.onap.cps.ncmp.event.model.BatchResponseEvent",
+ "eventSchema": "urn:cps:org.onap.cps.ncmp.event.model.BatchResponseEvent",
"eventSchemaVersion": "v1",
"event": {
"payload": [
diff --git a/dmi-plugin-stub/mappings/batchCmHandles.json b/dmi-plugin-stub/mappings/batchCmHandles.json
index 5f290cd47d..851959bf92 100644
--- a/dmi-plugin-stub/mappings/batchCmHandles.json
+++ b/dmi-plugin-stub/mappings/batchCmHandles.json
@@ -1,7 +1,7 @@
{
"request": {
"method": "POST",
- "urlPattern": "/dmi/v1/batch/data/ds/.*"
+ "urlPattern": "/dmi/v1/data?.*"
},
"response": {
"status": 200,