Merge "Introducing NCMP Facades"
diff --git a/checkstyle/pom.xml b/checkstyle/pom.xml
index 9ecfa2d..0f328e6 100644
--- a/checkstyle/pom.xml
+++ b/checkstyle/pom.xml
@@ -26,7 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>3.5.0-SNAPSHOT</version>
+    <version>3.5.1-SNAPSHOT</version>
 
     <profiles>
         <profile>
diff --git a/cps-application/pom.xml b/cps-application/pom.xml
index abcb88f..8ab314e 100644
--- a/cps-application/pom.xml
+++ b/cps-application/pom.xml
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml
index 4548eb2..93274b8 100644
--- a/cps-bom/pom.xml
+++ b/cps-bom/pom.xml
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>3.5.0-SNAPSHOT</version>
+    <version>3.5.1-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml
index dbb1128..380c00a 100644
--- a/cps-dependencies/pom.xml
+++ b/cps-dependencies/pom.xml
@@ -27,7 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>3.5.0-SNAPSHOT</version>
+    <version>3.5.1-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
diff --git a/cps-events/pom.xml b/cps-events/pom.xml
index ec0c96b..1af13cc 100644
--- a/cps-events/pom.xml
+++ b/cps-events/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ncmp-events/pom.xml b/cps-ncmp-events/pom.xml
index 3804e5b..befae04 100644
--- a/cps-ncmp-events/pom.xml
+++ b/cps-ncmp-events/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
index 7b277cd..6e21c04 100644
--- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
+++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
     </parent>
 
     <artifactId>cps-ncmp-rest-stub-app</artifactId>
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
index cb65532..15c7968 100644
--- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
+++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
     </parent>
     <artifactId>cps-ncmp-rest-stub-service</artifactId>
 
diff --git a/cps-ncmp-rest-stub/pom.xml b/cps-ncmp-rest-stub/pom.xml
index 441ce43..9fad293 100644
--- a/cps-ncmp-rest-stub/pom.xml
+++ b/cps-ncmp-rest-stub/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml
index e333344..7faec78 100644
--- a/cps-ncmp-rest/pom.xml
+++ b/cps-ncmp-rest/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index 8a8f7d0..1f94b34 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
index f221245..6ff79a9 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
@@ -20,9 +20,11 @@
 
 package org.onap.cps.ncmp.api.datajobs;
 
+import java.util.List;
 import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata;
 import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest;
 import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest;
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse;
 
 public interface DataJobService {
 
@@ -41,6 +43,8 @@
      * @param dataJobId           Unique identifier of the job within the request
      * @param dataJobMetadata     data job request headers
      * @param dataJobWriteRequest write data job request
+     * @return a list of sub-job write responses
      */
-    void writeDataJob(String dataJobId, DataJobMetadata dataJobMetadata, DataJobWriteRequest dataJobWriteRequest);
+    List<SubJobWriteResponse> writeDataJob(String dataJobId, DataJobMetadata dataJobMetadata,
+                                           DataJobWriteRequest dataJobWriteRequest);
 }
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java
new file mode 100644
index 0000000..7e9ca79
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.datajobs.models;
+
+import java.util.Map;
+
+/**
+ * Describes the write data job operation to be forwarded to dmi.
+ *
+ * @param path               Identifier of a managed object (MO) on a network element. Defines the resource on which
+ *                           operation is executed. Typically, is Fully Distinguished Name (FDN).
+ * @param op                 Describes the operation to execute.  The value can be as below:
+ *                           e.g. "add", "replace", "remove", "action" etc.
+ * @param moduleSetTag       The module set tag of the CM Handle.
+ * @param value              The value to be written depends on the type of operation.
+ * @param operationId        Unique identifier of the operation within the request.
+ * @param privateProperties  Contains the private properties of a Cm Handle.
+ */
+public record DmiWriteOperation(
+        String path,
+        String op,
+        String moduleSetTag,
+        Object value,
+        String operationId,
+        Map<String, String> privateProperties) {}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ProducerKey.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ProducerKey.java
new file mode 100644
index 0000000..ac6b7f8
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ProducerKey.java
@@ -0,0 +1,36 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.datajobs.models;
+
+/**
+ * Composite key created from the DMI Service name and a data producer identifier.
+ * Helps to group of the sub job request for a given DMI Plugin.
+ *
+ * @param dmiServiceName          Describes the name of the relevant DMI service.
+ * @param dataProducerIdentifier  The name of a data producer identifier from a Cm Handle.
+ */
+public record ProducerKey(String dmiServiceName, String dataProducerIdentifier) {
+
+    @Override
+    public String toString() {
+        return dmiServiceName + "#"  + dataProducerIdentifier;
+    }
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java
new file mode 100644
index 0000000..432b21b
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.datajobs.models;
+
+import java.util.Collection;
+
+/**
+ * Response data for a write operation by the DMI Plugin.
+ *
+ * @param dataAcceptType  Define the data response accept type.
+ *                        e.g. "application/vnd.3gpp.object-tree-hierarchical+json",
+ *                        "application/vnd.3gpp.object-tree-flat+json" etc.
+ * @param dataContentType Define the data request content type.
+ *                        e.g. "application/3gpp-json-patch+json" etc.
+ * @param dataProducerId  Identifier of the data producer.
+ *
+ * @param data            A collection of outgoing write operations.
+ */
+public record SubJobWriteRequest (
+        String dataAcceptType,
+        String dataContentType,
+        String dataProducerId,
+        Collection<DmiWriteOperation> data) {}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteResponse.java
new file mode 100644
index 0000000..9cdd8e0
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteResponse.java
@@ -0,0 +1,30 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.datajobs.models;
+
+/**
+ * Request data for a write operation towards DMI Plugin.
+ *
+ * @param subJobId        Identifier of the sub-job from DMI.
+ * @param dmiServiceName  The provided name of the DMI service from the request.
+ * @param dataProducerId  Identifier of the data producer.
+ */
+public record SubJobWriteResponse(String subJobId, String dmiServiceName, String dataProducerId) {}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
index 7db6c5c..56ed6e3 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
@@ -20,15 +20,27 @@
 
 package org.onap.cps.ncmp.impl.datajobs;
 
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.datajobs.DataJobService;
 import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata;
 import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest;
 import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest;
+import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation;
+import org.onap.cps.ncmp.api.datajobs.models.ProducerKey;
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse;
+import org.springframework.stereotype.Service;
 
 @Slf4j
+@Service
+@RequiredArgsConstructor
 public class DataJobServiceImpl implements DataJobService {
 
+    private final DmiSubJobRequestHandler dmiSubJobClient;
+    private final WriteRequestExaminer writeRequestExaminer;
+
     @Override
     public void readDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata,
                             final DataJobReadRequest dataJobReadRequest) {
@@ -36,8 +48,13 @@
     }
 
     @Override
-    public void writeDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata,
-                             final DataJobWriteRequest dataJobWriteRequest) {
+    public List<SubJobWriteResponse> writeDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata,
+                                                  final DataJobWriteRequest dataJobWriteRequest) {
         log.info("data job id for write operation is: {}", dataJobId);
+
+        final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey =
+                writeRequestExaminer.splitDmiWriteOperationsFromRequest(dataJobId, dataJobWriteRequest);
+
+        return dmiSubJobClient.sendRequestsToDmi(dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey);
     }
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java
new file mode 100644
index 0000000..69eadab
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java
@@ -0,0 +1,85 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.datajobs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata;
+import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation;
+import org.onap.cps.ncmp.api.datajobs.models.ProducerKey;
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteRequest;
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse;
+import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
+import org.onap.cps.ncmp.api.impl.config.DmiProperties;
+import org.onap.cps.ncmp.api.impl.operations.OperationType;
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DmiSubJobRequestHandler {
+
+    private final DmiRestClient dmiRestClient;
+    private final DmiProperties dmiProperties;
+    private final JsonObjectMapper jsonObjectMapper;
+    static final String NO_AUTH_HEADER = null;
+
+    /**
+     * Sends sub-job write requests to the DMI Plugin.
+     *
+     * @param dataJobId                        data ojb identifier
+     * @param dataJobMetadata                  the data job's metadata
+     * @param dmiWriteOperationsPerProducerKey a collection of write requests per producer key.
+     * @return a list of sub-job write responses
+     */
+    public List<SubJobWriteResponse> sendRequestsToDmi(final String dataJobId, final DataJobMetadata dataJobMetadata,
+                                     final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey) {
+        final List<SubJobWriteResponse> subJobWriteResponses = new ArrayList<>(dmiWriteOperationsPerProducerKey.size());
+        dmiWriteOperationsPerProducerKey.forEach((producerKey, dmi3ggpWriteOperations) -> {
+            final SubJobWriteRequest subJobWriteRequest = new SubJobWriteRequest(dataJobMetadata.dataAcceptType(),
+                    dataJobMetadata.dataContentType(), dataJobId, dmi3ggpWriteOperations);
+
+            final String dmiResourceUrl = getDmiResourceUrl(dataJobId, producerKey);
+            final ResponseEntity<Object> responseEntity = dmiRestClient.postOperationWithJsonData(
+                    RequiredDmiService.DATA,
+                    dmiResourceUrl,
+                    jsonObjectMapper.asJsonString(subJobWriteRequest),
+                    OperationType.CREATE,
+                    NO_AUTH_HEADER);
+            final SubJobWriteResponse subJobWriteResponse = (SubJobWriteResponse) responseEntity.getBody();
+            log.debug("Sub job write response: {}", subJobWriteResponse);
+            subJobWriteResponses.add(subJobWriteResponse);
+        });
+        return subJobWriteResponses;
+    }
+
+    private String getDmiResourceUrl(final String dataJobId, final ProducerKey producerKey) {
+        return DmiServiceUrlBuilder.newInstance().pathSegment("writeJob").variablePathSegment("requestId", dataJobId)
+                .build(producerKey.dmiServiceName(), dmiProperties.getDmiBasePath());
+    }
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java
new file mode 100644
index 0000000..1c3e476
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java
@@ -0,0 +1,109 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.datajobs;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest;
+import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation;
+import org.onap.cps.ncmp.api.datajobs.models.ProducerKey;
+import org.onap.cps.ncmp.api.datajobs.models.WriteOperation;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.utils.AlternateIdMatcher;
+import org.onap.cps.spi.model.DataNode;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class WriteRequestExaminer {
+
+    private final AlternateIdMatcher alternateIdMatcher;
+    private static final String PATH_SEPARATOR = "/";
+
+    /**
+     * Splitting incoming data job write request into Dmi Write Operations by ProducerKey.
+     *
+     * @param dataJobId data job identifier
+     * @param dataJobWriteRequest incoming data job write request
+     * @return {@code Map} map of Dmi Write Operations per Producer Key
+     */
+    public Map<ProducerKey, List<DmiWriteOperation>> splitDmiWriteOperationsFromRequest(
+            final String dataJobId,
+            final DataJobWriteRequest dataJobWriteRequest) {
+        final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey = new HashMap<>();
+        for (final WriteOperation writeOperation : dataJobWriteRequest.data()) {
+            examineWriteOperation(dataJobId, dmiWriteOperationsPerProducerKey, writeOperation);
+        }
+        return dmiWriteOperationsPerProducerKey;
+    }
+
+    private void examineWriteOperation(final String dataJobId,
+                                       final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey,
+                                       final WriteOperation writeOperation) {
+        log.debug("data job id for write operation is: {}", dataJobId);
+        final DataNode dataNode = alternateIdMatcher
+                .getCmHandleDataNodeByLongestMatchingAlternateId(writeOperation.path(), PATH_SEPARATOR);
+
+        final DmiWriteOperation dmiWriteOperation = createDmiWriteOperation(writeOperation, dataNode);
+
+        final ProducerKey producerKey = createProducerKey(dataNode);
+        final List<DmiWriteOperation> dmiWriteOperations;
+        if (dmiWriteOperationsPerProducerKey.containsKey(producerKey)) {
+            dmiWriteOperations = dmiWriteOperationsPerProducerKey.get(producerKey);
+        } else {
+            dmiWriteOperations = new ArrayList<>();
+            dmiWriteOperationsPerProducerKey.put(producerKey, dmiWriteOperations);
+        }
+        dmiWriteOperations.add(dmiWriteOperation);
+    }
+
+    private ProducerKey createProducerKey(final DataNode dataNode) {
+        return new ProducerKey((String) dataNode.getLeaves().get("dmi-service-name"),
+                (String) dataNode.getLeaves().get("data-producer-identifier"));
+    }
+
+    private DmiWriteOperation createDmiWriteOperation(final WriteOperation writeOperation,
+                                                      final DataNode dataNode) {
+        return new DmiWriteOperation(
+                writeOperation.path(),
+                writeOperation.op(),
+                (String) dataNode.getLeaves().get("module-set-tag"),
+                writeOperation.value(),
+                writeOperation.operationId(),
+                getPrivatePropertiesFromDataNode(dataNode));
+    }
+
+    private Map<String, String> getPrivatePropertiesFromDataNode(final DataNode dataNode) {
+        final YangModelCmHandle yangModelCmHandle = YangDataConverter.convertCmHandleToYangModel(dataNode);
+        final Map<String, String> cmHandleDmiProperties = new LinkedHashMap<>();
+        yangModelCmHandle.getDmiProperties()
+                .forEach(dmiProperty -> cmHandleDmiProperties.put(dmiProperty.getName(), dmiProperty.getValue()));
+        return cmHandleDmiProperties;
+    }
+
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java
index 8385f19..3fad684 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java
@@ -44,7 +44,7 @@
      * @param separator   a string that separates each element from the next.
      * @return data node
      */
-    public DataNode getCmHandleDataNodeByLongestMatchAlternateId(final String alternateId, final String separator) {
+    public DataNode getCmHandleDataNodeByLongestMatchingAlternateId(final String alternateId, final String separator) {
         String bestMatch = alternateId;
         while (StringUtils.isNotEmpty(bestMatch)) {
             try {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy
index bef0adc..9cee2bd 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy
@@ -25,6 +25,8 @@
 import ch.qos.logback.classic.spi.ILoggingEvent
 import ch.qos.logback.core.read.ListAppender
 import org.onap.cps.ncmp.impl.datajobs.DataJobServiceImpl
+import org.onap.cps.ncmp.impl.datajobs.DmiSubJobRequestHandler
+import org.onap.cps.ncmp.impl.datajobs.WriteRequestExaminer
 import org.slf4j.LoggerFactory
 import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest
 import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
@@ -33,9 +35,14 @@
 import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
 import spock.lang.Specification
 
-class DataJobServiceImplSpec extends Specification{
+class DataJobServiceImplSpec extends Specification {
 
-    def objectUnderTest = new DataJobServiceImpl()
+    def mockWriteRequestExaminer = Mock(WriteRequestExaminer)
+    def mockDmiSubJobRequestHandler = Mock(DmiSubJobRequestHandler)
+
+    def objectUnderTest = new DataJobServiceImpl(mockDmiSubJobRequestHandler, mockWriteRequestExaminer)
+
+    def myDataJobMetadata = new DataJobMetadata('', '', '')
 
     def logger = Spy(ListAppender<ILoggingEvent>)
 
@@ -47,28 +54,27 @@
         ((Logger) LoggerFactory.getLogger(DataJobServiceImpl.class)).detachAndStopAllAppenders()
     }
 
-    def '#operation data job request.'() {
-        given: 'data job metadata'
-            def dataJobMetadata = new DataJobMetadata('client-topic', 'application/vnd.3gpp.object-tree-hierarchical+json', 'application/3gpp-json-patch+json')
-        when: 'read/write data job request is processed'
-            if (operation == 'read') {
-                objectUnderTest.readDataJob('some-job-id', dataJobMetadata, new DataJobReadRequest([getWriteOrReadOperationRequest(operation)]))
-            } else {
-                objectUnderTest.writeDataJob('some-job-id', dataJobMetadata, new DataJobWriteRequest([getWriteOrReadOperationRequest(operation)]))
-            }
+    def 'Read data job request.'() {
+        when: 'read data job request is processed'
+            def readOperation = new ReadOperation('', '', '', [], [], '', '', 1)
+            objectUnderTest.readDataJob('my-job-id', myDataJobMetadata, new DataJobReadRequest([readOperation]))
         then: 'the data job id is correctly logged'
             def loggingEvent = logger.list[0]
             assert loggingEvent.level == Level.INFO
-            assert loggingEvent.formattedMessage.contains('data job id for ' + operation + ' operation is: some-job-id')
-        where: 'the following data job operations are used'
-            operation << ['read', 'write']
+            assert loggingEvent.formattedMessage.contains('data job id for read operation is: my-job-id')
     }
 
-    def getWriteOrReadOperationRequest(operation) {
-        if (operation == 'write') {
-            return new WriteOperation('some/write/path', 'add', 'some-operation-id', 'some-value')
-        }
-        return new ReadOperation('some/read/path', 'read', 'some-operation-id', ['some-attrib-1'], ['some-field-1'], 'some-filter', 'some-scope-type', 1)
+    def 'Write data-job request.'() {
+        given: 'data job metadata and write request'
+            def dataJobWriteRequest = new DataJobWriteRequest([new WriteOperation('', '', '', null)])
+        and: 'a map of producer key and dmi 3gpp write operation'
+            def dmiWriteOperationsPerProducerKey = [:]
+        when: 'write data job request is processed'
+            objectUnderTest.writeDataJob('my-job-id', myDataJobMetadata, dataJobWriteRequest)
+        then: 'the examiner service is called and a map is returned'
+            1 * mockWriteRequestExaminer.splitDmiWriteOperationsFromRequest('my-job-id', dataJobWriteRequest) >> dmiWriteOperationsPerProducerKey
+        and: 'the dmi request handler is called with the result from the examiner'
+            1 * mockDmiSubJobRequestHandler.sendRequestsToDmi('my-job-id', myDataJobMetadata, dmiWriteOperationsPerProducerKey)
     }
 
     def setupLogger() {
@@ -77,4 +83,4 @@
         setupLogger.addAppender(logger)
         logger.start()
     }
-}
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy
new file mode 100644
index 0000000..a1e0329
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DmiSubJobRequestHandlerSpec.groovy
@@ -0,0 +1,41 @@
+package org.onap.cps.ncmp.api.impl
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata
+import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation
+import org.onap.cps.ncmp.api.datajobs.models.ProducerKey
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse
+import org.onap.cps.ncmp.api.impl.client.DmiRestClient
+import org.onap.cps.ncmp.api.impl.config.DmiProperties
+import org.onap.cps.ncmp.api.impl.operations.OperationType
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService
+import org.onap.cps.ncmp.impl.datajobs.DmiSubJobRequestHandler
+import org.onap.cps.utils.JsonObjectMapper
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import spock.lang.Specification
+
+class DmiSubJobRequestHandlerSpec extends Specification {
+
+    def mockDmiRestClient = Mock(DmiRestClient)
+    def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+    def mockDmiProperties = Mock(DmiProperties)
+    def static NO_AUTH = null
+    def objectUnderTest = new DmiSubJobRequestHandler(mockDmiRestClient, mockDmiProperties, jsonObjectMapper)
+
+    def 'Send a sub-job request to the DMI Plugin.'() {
+        given: 'a data job id, metadata and a map of producer keys and write operations to create a request'
+            def dataJobId = 'some-job-id'
+            def dataJobMetadata = new DataJobMetadata('', '', '')
+            def dmiWriteOperation = new DmiWriteOperation('', '', '', null, '', [:])
+            def dmiWriteOperationsPerProducerKey = [new ProducerKey('', ''): [dmiWriteOperation]]
+            def response = new ResponseEntity<>(new SubJobWriteResponse('my-sub-job-id', '', ''), HttpStatus.OK)
+        when: 'sending request to DMI invoked'
+            objectUnderTest.sendRequestsToDmi(dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey)
+        then: 'the dmi rest client is called'
+            1 * mockDmiRestClient.postOperationWithJsonData(RequiredDmiService.DATA, _, _, OperationType.CREATE, NO_AUTH) >> response
+        and: 'the result contains the expected sub-job write responses'
+            def result = response.body
+            assert result.subJobId() == 'my-sub-job-id'
+    }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy
new file mode 100644
index 0000000..d5d6339
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/WriteRequestExaminerSpec.groovy
@@ -0,0 +1,63 @@
+
+package org.onap.cps.ncmp.impl.datajobs
+
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
+import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
+import org.onap.cps.ncmp.utils.AlternateIdMatcher
+import org.onap.cps.spi.model.DataNode
+import spock.lang.Specification
+
+class WriteRequestExaminerSpec extends Specification {
+
+    def mockAlternateIdMatcher = Mock(AlternateIdMatcher)
+    def objectUnderTest = new WriteRequestExaminer(mockAlternateIdMatcher)
+
+    def setup() {
+        def ch1 = new DataNode(leaves: [id: 'ch1', 'dmi-service-name': 'dmiA', 'data-producer-identifier': 'p1'])
+        def ch2 = new DataNode(leaves: [id: 'ch2', 'dmi-service-name': 'dmiA', 'data-producer-identifier': 'p1'])
+        def ch3 = new DataNode(leaves: [id: 'ch3', 'dmi-service-name': 'dmiA', 'data-producer-identifier': 'p2'])
+        def ch4 = new DataNode(leaves: [id: 'ch4', 'dmi-service-name': 'dmiB', 'data-producer-identifier': 'p1'])
+        mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn1', '/') >> ch1
+        mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn2', '/') >> ch2
+        mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn3', '/') >> ch3
+        mockAlternateIdMatcher.getCmHandleDataNodeByLongestMatchingAlternateId('fdn4', '/') >> ch4
+    }
+
+    def 'Create a map of dmi write requests per producer key with #scenario.'() {
+        given: 'a write request with some write operations'
+            def writeOperations = writeOperationFdns.collect {
+                new WriteOperation(it, '', '', null)
+            }
+        and: 'operations are wrapped in a write request'
+            def dataJobWriteRequest = new DataJobWriteRequest(writeOperations)
+        when: 'the DMI write operations are split from the request'
+            def dmiWriteOperationsPerProducerKey = objectUnderTest.splitDmiWriteOperationsFromRequest('some id', dataJobWriteRequest)
+        then: 'we get the expected number of keys and values.'
+            def producerKeysAsStrings = dmiWriteOperationsPerProducerKey.keySet().collect {
+                it.toString()
+            }
+            assert producerKeysAsStrings.size() == expectedKeys.size()
+            assert expectedKeys.containsAll(producerKeysAsStrings)
+        where:
+            scenario                                                          | writeOperationFdns               || expectedKeys
+            'one fdn'                                                         | ['fdn1']                         || ['dmiA#p1']
+            'a duplicated target'                                             | ['fdn1','fdn1']                  || ['dmiA#p1']
+            'two different targets'                                           | ['fdn1','fdn2']                  || ['dmiA#p1']
+            'two different targets and different producer keys'               | ['fdn1','fdn3']                  || ['dmiA#p1', 'dmiA#p2']
+            'two different targets and different DMI services'                | ['fdn1','fdn4']                  || ['dmiA#p1', 'dmiB#p1']
+            'many targets with different dmi service names and producer keys' | ['fdn1', 'fdn2', 'fdn3', 'fdn4'] || ['dmiA#p1', 'dmiA#p2', 'dmiB#p1']
+    }
+
+    def 'Validate the ordering of the created sub jobs.'() {
+        given: 'a few write operations for the same producer'
+            def writeOperations = (1..3).collect {
+                new WriteOperation('fdn1', '', it.toString(), null)
+            }
+        and: 'operation is wrapped in a write request'
+            def dataJobWriteRequest = new DataJobWriteRequest(writeOperations)
+        when: 'the DMI write operations are split from the request'
+            def dmiWriteOperations = objectUnderTest.splitDmiWriteOperationsFromRequest('some id', dataJobWriteRequest).values().iterator().next()
+        then: 'we get the operation ids in the expected order.'
+            assert dmiWriteOperations.operationId == ['1', '2', '3']
+    }
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy
index 720a7e7..3422610 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy
@@ -40,7 +40,7 @@
 
     def 'Finding longest alternate id matches.'() {
         expect: 'querying for alternate id a matching result found'
-            assert objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(targetAlternateId, '/') != null
+            assert objectUnderTest.getCmHandleDataNodeByLongestMatchingAlternateId(targetAlternateId, '/') != null
         where: 'the following parameters are used'
             scenario                   | targetAlternateId
             'exact match'              | '/a/b'
@@ -51,7 +51,7 @@
 
     def 'Attempt to find longest alternate id match without any matches.'() {
         when: 'attempt to find alternateId'
-            objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(targetAlternateId, '/')
+            objectUnderTest.getCmHandleDataNodeByLongestMatchingAlternateId(targetAlternateId, '/')
         then: 'no alternate id match found exception thrown'
             def thrown = thrown(NoAlternateIdMatchFoundException)
         and: 'the exception has the relevant details from the error response'
diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml
index 594aa1f..591095a 100644
--- a/cps-parent/pom.xml
+++ b/cps-parent/pom.xml
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.5.0-SNAPSHOT</version>
+    <version>3.5.1-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml
index 3176d9a..633ec6b 100644
--- a/cps-path-parser/pom.xml
+++ b/cps-path-parser/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml
index ccfb77f..f7ddd6c 100644
--- a/cps-rest/pom.xml
+++ b/cps-rest/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml
index 221e1a9..14a091f 100644
--- a/cps-ri/pom.xml
+++ b/cps-ri/pom.xml
@@ -26,7 +26,7 @@
     <parent>

         <groupId>org.onap.cps</groupId>

         <artifactId>cps-parent</artifactId>

-        <version>3.5.0-SNAPSHOT</version>

+        <version>3.5.1-SNAPSHOT</version>

         <relativePath>../cps-parent/pom.xml</relativePath>

     </parent>

 

diff --git a/cps-service/pom.xml b/cps-service/pom.xml
index 4224968..2564ceb 100644
--- a/cps-service/pom.xml
+++ b/cps-service/pom.xml
@@ -29,7 +29,7 @@
   <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.5.0-SNAPSHOT</version>
+    <version>3.5.1-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
   </parent>
 
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml
index e62c01d..3a678f6 100644
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml
+++ b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
     </parent>
 
     <artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml
index c0b4ead..73469de 100644
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml
+++ b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
     </parent>
     <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
 
diff --git a/dmi-plugin-demo-and-csit-stub/pom.xml b/dmi-plugin-demo-and-csit-stub/pom.xml
index 0719272..a61c985 100644
--- a/dmi-plugin-demo-and-csit-stub/pom.xml
+++ b/dmi-plugin-demo-and-csit-stub/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/docs/api/swagger/cps/openapi.yaml b/docs/api/swagger/cps/openapi.yaml
index 2798b78..7cba581 100644
--- a/docs/api/swagger/cps/openapi.yaml
+++ b/docs/api/swagger/cps/openapi.yaml
@@ -1346,6 +1346,13 @@
         schema:
           example: 2021-03-21T00:10:34.030-0100
           type: string
+      - description: Content type header
+        in: header
+        name: Content-Type
+        required: true
+        schema:
+          example: application/json
+          type: string
       requestBody:
         content:
           application/json:
@@ -1354,7 +1361,16 @@
                 $ref: '#/components/examples/dataSample'
                 value: null
             schema:
+              type: string
+          application/xml:
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSampleXml'
+                value: null
+            schema:
               type: object
+              xml:
+                name: stores
         required: true
       responses:
         "200":
@@ -1896,7 +1912,7 @@
       summary: Replace list content
       tags:
       - cps-data
-  /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/delta:
+  /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/deltaAnchors:
     get:
       description: Get delta between two anchors within a given dataspace
       operationId: getDeltaByDataspaceAndAnchors
@@ -1989,6 +2005,96 @@
       tags:
       - cps-data
       x-codegen-request-body-name: xpath
+  /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/deltaPayload:
+    post:
+      description: Get delta between an anchor in a dataspace and JSON payload
+      operationId: getDeltaByDataspaceAnchorAndPayload
+      parameters:
+      - description: dataspace-name
+        in: path
+        name: dataspace-name
+        required: true
+        schema:
+          example: my-dataspace
+          type: string
+      - description: anchor-name
+        in: path
+        name: anchor-name
+        required: true
+        schema:
+          example: my-anchor
+          type: string
+      - description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/xpath.html"
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+        in: query
+        name: xpath
+        required: false
+        schema:
+          default: /
+          type: string
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              $ref: '#/components/schemas/getDeltaByDataspaceAnchorAndPayload_request'
+      responses:
+        "200":
+          content:
+            application/json:
+              examples:
+                dataSample:
+                  $ref: '#/components/examples/deltaReportSample'
+                  value: null
+              schema:
+                type: object
+          description: OK
+        "400":
+          content:
+            application/json:
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Bad Request
+        "401":
+          content:
+            application/json:
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Unauthorized
+        "403":
+          content:
+            application/json:
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Forbidden
+        "500":
+          content:
+            application/json:
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Internal Server Error
+      summary: Get delta between an anchor and JSON payload
+      tags:
+      - cps-data
   /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes/query:
     get:
       deprecated: true
@@ -2270,7 +2376,7 @@
     dataSampleXml:
       value: <stores xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <bookstore xmlns="org:onap:ccsdk:sample">
         <bookstore-name>Chapters</bookstore-name> <categories> <code>1</code> <name>SciFi</name>
-        </categories> </bookstore> </stores>
+        <code>2</code> <name>kids</name> </categories> </bookstore> </stores>
     deltaReportSample:
       value:
       - action: ADD
@@ -2530,6 +2636,16 @@
           schema:
             type: object
       description: OK
+    Unauthorized:
+      content:
+        application/json:
+          example:
+            status: 401
+            message: Unauthorized request
+            details: This request is unauthorized
+          schema:
+            $ref: '#/components/schemas/ErrorMessage'
+      description: Unauthorized
   schemas:
     ErrorMessage:
       properties:
@@ -2619,6 +2735,24 @@
           type: string
       title: Module reference object
       type: object
+    getDeltaByDataspaceAnchorAndPayload_request:
+      properties:
+        json:
+          example:
+            test:bookstore:
+              bookstore-name: Chapters
+              categories:
+              - code: 1
+                name: SciFi
+              - code: 2
+                name: kids
+          type: object
+        file:
+          format: binary
+          type: string
+      required:
+      - json
+      type: object
   securitySchemes:
     basicAuth:
       scheme: basic
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index b9df799..e14a725 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -12,12 +12,12 @@
     :depth: 2
 ..
 
-..      =========================
-..      * * *   NEW DELHI   * * *
-..      =========================
+..      ====================
+..      * * *   OSLO   * * *
+..      ====================
 
-Version: 3.4.10
-===============
+Version: 3.5.1
+==============
 
 Release Data
 ------------
@@ -26,10 +26,10 @@
 | **CPS Project**                      |                                                        |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
-| **Docker images**                    | onap/cps-and-ncmp:3.4.10                               |
+| **Docker images**                    | onap/cps-and-ncmp:3.5.1                                |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
-| **Release designation**              | 3.4.10 New Delhi                                       |
+| **Release designation**              | 3.5.1 Oslo                                             |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
 | **Release date**                     | Not yet released                                       |
@@ -38,10 +38,45 @@
 
 Bug Fixes
 ---------
-3.4.10
+3.5.1
 
 Features
 --------
+3.5.1
+
+Version: 3.5.0
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project**                      |                                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images**                    | onap/cps-and-ncmp:3.5.0                                |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation**              | 3.5.0 Oslo                                             |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release date**                     | 2024 June 20                                           |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.5.0
+
+Features
+--------
+3.5.0
+    - `CPS-989 <https://jira.onap.org/browse/CPS-989>`_ Replace RestTemplate with WebClient.
+    - `CPS-2172 <https://jira.onap.org/browse/CPS-2172>`_ Support for OpenTelemetry Tracing.
+
+..      =========================
+..      * * *   NEW DELHI   * * *
+..      =========================
 
 Version: 3.4.9
 ==============
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
index f228e01..a311266 100644
--- a/integration-test/pom.xml
+++ b/integration-test/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryByAlternateIdPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryByAlternateIdPerfTest.groovy
index 9504c9e..ddc232d 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryByAlternateIdPerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryByAlternateIdPerfTest.groovy
@@ -41,7 +41,7 @@
         when: 'an alternate id as cps path query'
             resourceMeter.start()
             def cpsPath = "/a/b/c/d-5/e/f/g/h/i"
-            def dataNodes = objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(cpsPath, '/')
+            def dataNodes = objectUnderTest.getCmHandleDataNodeByLongestMatchingAlternateId(cpsPath, '/')
         and: 'the ids of the result are extracted and converted to xpath'
             def cpsXpaths = dataNodes.stream().map(dataNode -> "/dmi-registry/cm-handles[@id='${dataNode.leaves.id}']".toString() ).collect(Collectors.toSet())
         and: 'a single get is executed to get all the parent objects and their descendants'
diff --git a/jacoco-report/pom.xml b/jacoco-report/pom.xml
index 447614f..01d47bf 100644
--- a/jacoco-report/pom.xml
+++ b/jacoco-report/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.5.0-SNAPSHOT</version>
+        <version>3.5.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/pom.xml b/pom.xml
index 4d04602..fcead19 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
 

     <groupId>org.onap.cps</groupId>

     <artifactId>cps-aggregator</artifactId>

-    <version>3.5.0-SNAPSHOT</version>

+    <version>3.5.1-SNAPSHOT</version>

     <packaging>pom</packaging>

 

     <name>cps</name>

diff --git a/releases/3.5.0-container.yaml b/releases/3.5.0-container.yaml
new file mode 100644
index 0000000..d6e3b63
--- /dev/null
+++ b/releases/3.5.0-container.yaml
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.5.0
+project: cps
+log_dir: cps-maven-docker-stage-master/942/
+ref: 59575279e36572be4386f1c644db52fcb570fc9e
+containers:
+  - name: 'cps-and-ncmp'
+    version: '3.5.0-20240620T123504Z'
\ No newline at end of file
diff --git a/releases/3.5.0.yaml b/releases/3.5.0.yaml
new file mode 100644
index 0000000..81dc746
--- /dev/null
+++ b/releases/3.5.0.yaml
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/950/
+project: cps
+version: 3.5.0
\ No newline at end of file
diff --git a/spotbugs/pom.xml b/spotbugs/pom.xml
index bab9551..7edfaac 100644
--- a/spotbugs/pom.xml
+++ b/spotbugs/pom.xml
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>3.5.0-SNAPSHOT</version>
+    <version>3.5.1-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
diff --git a/version.properties b/version.properties
index 5fdc4e3..6345596 100644
--- a/version.properties
+++ b/version.properties
@@ -22,7 +22,7 @@
 
 major=3
 minor=5
-patch=0
+patch=1
 
 base_version=${major}.${minor}.${patch}