Merge "Updated the Documentation with sample yang and json"
authorToine Siebelink <toine.siebelink@est.tech>
Mon, 28 Mar 2022 11:03:31 +0000 (11:03 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 28 Mar 2022 11:03:31 +0000 (11:03 +0000)
46 files changed:
checkstyle/pom.xml
cps-application/pom.xml
cps-bom/pom.xml
cps-dependencies/pom.xml
cps-events/pom.xml
cps-ncmp-rest/pom.xml
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandlesList.java [deleted file]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy [new file with mode: 0644]
cps-parent/pom.xml
cps-path-parser/pom.xml
cps-rest/pom.xml
cps-ri/pom.xml
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java [new file with mode: 0644]
cps-ri/src/main/resources/hibernate.cfg.xml [new file with mode: 0644]
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy [new file with mode: 0644]
cps-ri/src/test/resources/hibernate.cfg.xml [new file with mode: 0644]
cps-service/pom.xml
cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
docs/admin-guide.rst
docs/api/swagger/cps/openapi.yaml
docs/api/swagger/ncmp/openapi-inventory.yaml
docs/api/swagger/ncmp/openapi.yaml
docs/deployment.rst
docs/index.rst
docs/overview.rst
docs/release-notes.rst
jacoco-report/pom.xml
pom.xml
releases/3.0.0-container.yaml [new file with mode: 0644]
releases/3.0.0.yaml [new file with mode: 0644]
spotbugs/pom.xml
version.properties

index 07e6cf9..4e042e9 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
         <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
     </properties>
 
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-deploy-plugin</artifactId>
+                    <version>2.8.2</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
     <distributionManagement>
         <repository>
             <id>ecomp-releases</id>
index 50b06b2..193599f 100755 (executable)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 3e5f70d..e468926 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
         <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
     </properties>
 
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-deploy-plugin</artifactId>
+                    <version>2.8.2</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
     <distributionManagement>
         <repository>
             <id>ecomp-releases</id>
index f04213d..d3a033f 100755 (executable)
   ============LICENSE_END=========================================================
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
         <mapstruct.version>1.4.2.Final</mapstruct.version>
     </properties>
 
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-deploy-plugin</artifactId>
+                    <version>2.8.2</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
     <distributionManagement>
         <repository>
             <id>ecomp-releases</id>
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-dependencies</artifactId>
-                <version>2.5.5</version>
+                <version>2.6.4</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
             <dependency>
                 <groupId>org.springframework.cloud</groupId>
                 <artifactId>spring-cloud-dependencies</artifactId>
-                <version>2020.0.2</version>
+                <version>2021.0.1</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
                 <version>0.18.0</version>
                 <scope>test</scope>
             </dependency>
-            <dependency>
-                <groupId>org.apache.logging.log4j</groupId>
-                <artifactId>log4j-api</artifactId>
-                <version>2.17.1</version>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.logging.log4j</groupId>
-                <artifactId>log4j-to-slf4j</artifactId>
-                <version>2.17.1</version>
-            </dependency>
             <dependency>
                 <groupId>org.mapstruct</groupId>
                 <artifactId>mapstruct</artifactId>
index b9b399c..9bd9588 100644 (file)
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 97305cf..6a700c3 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index fe061ea..573c76e 100644 (file)
@@ -26,7 +26,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index d942d26..7f4c18f 100644 (file)
@@ -3,6 +3,7 @@
  *  Copyright (C) 2021 highstreet technologies GmbH
  *  Modifications Copyright (C) 2021-2022 Nordix Foundation
  *  Modifications Copyright (C) 2021 Pantheon.tech
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -26,6 +27,7 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum
 
 import java.util.Collection;
 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.spi.model.ModuleReference;
 
@@ -38,8 +40,9 @@ public interface NetworkCmProxyDataService {
      * Registration of New CM Handles.
      *
      * @param dmiPluginRegistration Dmi Plugin Registration
+     * @return dmiPluginRegistrationResponse
      */
-    void updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
+    DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
 
     /**
      * Get resource data for data store pass-through operational
index 76d4cef..c3369d8 100755 (executable)
@@ -3,7 +3,7 @@
  *  Copyright (C) 2021 highstreet technologies GmbH
  *  Modifications Copyright (C) 2021-2022 Nordix Foundation
  *  Modifications Copyright (C) 2021 Pantheon.tech
- *  Modifications Copyright (C) 2021 Bell Canada
+ *  Modifications Copyright (C) 2021-2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMES
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -53,11 +53,14 @@ import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations;
 import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
 import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandlesList;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 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.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
-import org.onap.cps.spi.exceptions.DataValidationException;
+import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.http.HttpStatus;
@@ -92,22 +95,22 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private static final String NO_TOPIC = null;
 
     @Override
-    public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) {
+    public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
+        final DmiPluginRegistration dmiPluginRegistration) {
         dmiPluginRegistration.validateDmiPluginRegistration();
-        try {
-            if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
-                parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration);
-            }
-            if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
-                parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration);
-            }
-            parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration);
-        } catch (final JsonProcessingException | DataNodeNotFoundException e) {
-            final String errorMessage = String.format(
-                    "Error occurred while processing the CM-handle registration request, caused by : [%s]",
-                    e.getMessage());
-            throw new DataValidationException(errorMessage, e.getMessage(), e);
+        final var dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
+        dmiPluginRegistrationResponse.setRemovedCmHandles(
+            parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles()));
+        if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
+            dmiPluginRegistrationResponse.setCreatedCmHandles(
+                parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration));
         }
+        if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
+            dmiPluginRegistrationResponse.setUpdatedCmHandles(
+                networkCmProxyDataServicePropertyHandler
+                    .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
+        }
+        return dmiPluginRegistrationResponse;
     }
 
     @Override
@@ -162,6 +165,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     /**
      * Retrieve cm handle details for a given cm handle.
+     *
      * @param cmHandleId cm handle identifier
      * @return cm handle details
      */
@@ -203,14 +207,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
      * THis method registers a cm handle and initiates modules sync.
      *
      * @param dmiPluginRegistration dmi plugin registration information.
-     * @throws JsonProcessingException thrown if json is malformed or missing.
+     * @return cm-handle registration response for create cm-handle requests.
      */
-    public void parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
-        final DmiPluginRegistration dmiPluginRegistration) throws JsonProcessingException {
-        final YangModelCmHandlesList createdYangModelCmHandlesList =
-            getUpdatedYangModelCmHandlesList(dmiPluginRegistration,
-                dmiPluginRegistration.getCreatedCmHandles());
-        registerAndSyncNewCmHandles(createdYangModelCmHandlesList);
+    public List<CmHandleRegistrationResponse> parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
+        final DmiPluginRegistration dmiPluginRegistration) {
+        return dmiPluginRegistration.getCreatedCmHandles().stream()
+            .map(cmHandle ->
+                YangModelCmHandle.toYangModelCmHandle(
+                    dmiPluginRegistration.getDmiPlugin(),
+                    dmiPluginRegistration.getDmiDataPlugin(),
+                    dmiPluginRegistration.getDmiModelPlugin(), cmHandle)
+            )
+            .map(this::registerAndSyncNewCmHandle)
+            .collect(Collectors.toList());
     }
 
     private static Object handleResponse(final ResponseEntity<?> responseEntity,
@@ -224,27 +233,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
-    private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
-        networkCmProxyDataServicePropertyHandler.updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles());
-    }
-
-    private YangModelCmHandlesList getUpdatedYangModelCmHandlesList(
-        final DmiPluginRegistration dmiPluginRegistration,
-        final List<NcmpServiceCmHandle> updatedCmHandles) {
-        return YangModelCmHandlesList.toYangModelCmHandlesList(
-            dmiPluginRegistration.getDmiPlugin(),
-            dmiPluginRegistration.getDmiDataPlugin(),
-            dmiPluginRegistration.getDmiModelPlugin(),
-            updatedCmHandles);
-    }
-
-    private void registerAndSyncNewCmHandles(final YangModelCmHandlesList yangModelCmHandlesList) {
-        final String cmHandleJsonData = jsonObjectMapper.asJsonString(yangModelCmHandlesList);
-        cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
+    private CmHandleRegistrationResponse registerAndSyncNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
+        try {
+            final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
+                jsonObjectMapper.asJsonString(yangModelCmHandle));
+            cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
                 cmHandleJsonData, NO_TIMESTAMP);
-
-        for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandlesList.getYangModelCmHandles()) {
             syncModulesAndCreateAnchor(yangModelCmHandle);
+            return CmHandleRegistrationResponse.createSuccessResponse(yangModelCmHandle.getId());
+        } catch (final AlreadyDefinedException alreadyDefinedException) {
+            return CmHandleRegistrationResponse.createFailureResponse(
+                yangModelCmHandle.getId(), RegistrationError.CM_HANDLE_ALREADY_EXIST);
+        } catch (final Exception exception) {
+            return CmHandleRegistrationResponse.createFailureResponse(yangModelCmHandle.getId(), exception);
         }
     }
 
@@ -253,24 +254,37 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         createAnchor(yangModelCmHandle);
     }
 
-    private void parseAndRemoveCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
-        for (final String cmHandle : dmiPluginRegistration.getRemovedCmHandles()) {
+    protected List<CmHandleRegistrationResponse> parseAndRemoveCmHandlesInDmiRegistration(
+        final List<String> tobeRemovedCmHandles) {
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
+            new ArrayList<>(tobeRemovedCmHandles.size());
+        for (final String cmHandle : tobeRemovedCmHandles) {
             try {
-                attemptToDeleteSchemaSetWithCascade(cmHandle);
+                deleteSchemaSetWithCascade(cmHandle);
                 cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
                     "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
-            } catch (final DataNodeNotFoundException e) {
-                log.warn("Datanode {} not deleted message {}", cmHandle, e.getMessage());
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
+            } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
+                log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
+                    cmHandle, dataNodeNotFoundException.getMessage());
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
+                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+            } catch (final Exception exception) {
+                log.error("Unable to de-register cm-handleIdd : {} , caused by : {}",
+                    cmHandle, exception.getMessage());
+                cmHandleRegistrationResponses.add(
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
             }
         }
+        return cmHandleRegistrationResponses;
     }
 
-    private void attemptToDeleteSchemaSetWithCascade(final String schemaSetName) {
+    private void deleteSchemaSetWithCascade(final String schemaSetName) {
         try {
             cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
                 CASCADE_DELETE_ALLOWED);
-        } catch (final Exception e) {
-            log.warn("Schema set {} delete failed, reason {}", schemaSetName, e.getMessage());
+        } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
+            log.warn("Schema set {} does not exist or already deleted", schemaSetName);
         }
     }
 
index ca2f578..c838a75 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Nordix Foundation
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,15 +29,19 @@ import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI
 import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
 
 import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsDataService;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
@@ -61,23 +66,31 @@ public class NetworkCmProxyDataServicePropertyHandler {
      *
      * @param ncmpServiceCmHandles collection of ncmpServiceCmHandles
      */
-    public void updateCmHandleProperties(final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles)
-        throws DataNodeNotFoundException {
+    public List<CmHandleRegistrationResponse> updateCmHandleProperties(
+        final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
+            final String cmHandle = ncmpServiceCmHandle.getCmHandleID();
             try {
-                final String cmHandleXpath = String.format(CM_HANDLE_XPATH_TEMPLATE,
-                    ncmpServiceCmHandle.getCmHandleID());
+                final String cmHandleXpath = String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandle);
                 final DataNode existingCmHandleDataNode =
                         cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXpath,
                                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
                 processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle);
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
             } catch (final DataNodeNotFoundException e) {
                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
-                    ncmpServiceCmHandle.getCmHandleID(),
-                        e.getMessage());
-                throw e;
+                    cmHandle, e.getMessage());
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
+                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+            } catch (final Exception exception) {
+                log.error("Unable to update dataNode for cmHandleId : {} , caused by : {}",
+                    cmHandle, exception.getMessage());
+                cmHandleRegistrationResponses.add(
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
             }
         }
+        return cmHandleRegistrationResponses;
     }
 
     private void processUpdates(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle incomingCmHandle) {
index 47062b3..e46b9e3 100644 (file)
@@ -21,6 +21,8 @@
 
 package org.onap.cps.ncmp.api.impl.yangmodels;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Strings;
 import java.util.ArrayList;
@@ -41,6 +43,7 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 @Getter
 @Setter
 @NoArgsConstructor
+@JsonInclude(Include.NON_NULL)
 public class YangModelCmHandle {
 
     private String id;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandlesList.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandlesList.java
deleted file mode 100644 (file)
index 261a018..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 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.yangmodels;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import lombok.Getter;
-import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-
-@Getter
-public class YangModelCmHandlesList {
-
-    @JsonProperty("cm-handles")
-    private final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>();
-
-    /**
-     * Create a YangModelCmHandleList given all service names and a collection of cmHandles.
-     * @param dmiServiceName the dmi service name
-     * @param dmiDataServiceName the dmi data service name
-     * @param dmiModelServiceName the dmi model service name
-     * @param ncmpServiceCmHandles cm handles rest model
-     * @return instance of YangModelCmHandleList
-     */
-    public static YangModelCmHandlesList toYangModelCmHandlesList(final String dmiServiceName,
-                                                                  final String dmiDataServiceName,
-                                                                  final String dmiModelServiceName,
-                                                                  final Collection<NcmpServiceCmHandle>
-                                                            ncmpServiceCmHandles) {
-        final YangModelCmHandlesList yangModelCmHandlesList = new YangModelCmHandlesList();
-        for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
-            final YangModelCmHandle yangModelCmHandle =
-                YangModelCmHandle.toYangModelCmHandle(
-                    dmiServiceName,
-                    dmiDataServiceName,
-                    dmiModelServiceName,
-                    ncmpServiceCmHandle);
-            yangModelCmHandlesList.add(yangModelCmHandle);
-        }
-        return yangModelCmHandlesList;
-    }
-
-    /**
-     * Add a yangModelCmHandle.
-     *
-     * @param yangModelCmHandle the yangModelCmHandle to add
-     */
-    public void add(final YangModelCmHandle yangModelCmHandle) {
-        yangModelCmHandles.add(yangModelCmHandle);
-    }
-}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java
new file mode 100644 (file)
index 0000000..e183ed1
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Bell Canada
+ *  ================================================================================
+ *  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 lombok.Builder;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@Data
+@Builder
+public class CmHandleRegistrationResponse {
+
+    private final String cmHandle;
+    private final Status status;
+    private RegistrationError registrationError;
+    private String errorText;
+
+    /**
+     * Creates a failure response based on exception.
+     *
+     * @param cmHandle  cmHandle
+     * @param exception exception
+     * @return CmHandleRegistrationResponse
+     */
+    public static CmHandleRegistrationResponse createFailureResponse(final String cmHandle, final Exception exception) {
+        return CmHandleRegistrationResponse.builder()
+            .cmHandle(cmHandle)
+            .status(Status.FAILURE)
+            .registrationError(RegistrationError.UNKNOWN_ERROR)
+            .errorText(exception.getMessage()).build();
+    }
+
+    /**
+     * Creates a failure response based on registration error.
+     *
+     * @param cmHandle          cmHandle
+     * @param registrationError registrationError
+     * @return CmHandleRegistrationResponse
+     */
+    public static CmHandleRegistrationResponse createFailureResponse(final String cmHandle,
+        final RegistrationError registrationError) {
+        return CmHandleRegistrationResponse.builder().cmHandle(cmHandle)
+            .status(Status.FAILURE)
+            .registrationError(registrationError)
+            .errorText(registrationError.errorText)
+            .build();
+    }
+
+    public static CmHandleRegistrationResponse createSuccessResponse(final String cmHandle) {
+        return CmHandleRegistrationResponse.builder().cmHandle(cmHandle)
+            .status(Status.SUCCESS).build();
+    }
+
+    public enum Status {
+        SUCCESS, FAILURE;
+    }
+
+    @RequiredArgsConstructor
+    public enum RegistrationError {
+        UNKNOWN_ERROR("00", "Unknown error"),
+        CM_HANDLE_ALREADY_EXIST("01", "cm-handle already exists"),
+        CM_HANDLE_DOES_NOT_EXIST("02", "cm-handle does not exist");
+
+        public final String errorCode;
+        public final String errorText;
+
+    }
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java
new file mode 100644 (file)
index 0000000..ce2f3e6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Bell Canada
+ *  ================================================================================
+ *  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 java.util.List;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class DmiPluginRegistrationResponse {
+    private List<CmHandleRegistrationResponse> createdCmHandles;
+    private List<CmHandleRegistrationResponse> updatedCmHandles;
+    private List<CmHandleRegistrationResponse> removedCmHandles;
+}
\ No newline at end of file
index e410463..e7c1d05 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -20,8 +21,8 @@
 
 package org.onap.cps.ncmp.api.impl
 
-import com.fasterxml.jackson.core.JsonProcessingException
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
@@ -29,14 +30,20 @@ import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.spi.exceptions.AlreadyDefinedException
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
-import org.onap.cps.spi.exceptions.DataValidationException
+import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Shared
 import spock.lang.Specification
 
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_ALREADY_EXIST
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
 
 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
@@ -57,102 +64,54 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockYangModelCmHandleRetriever = Mock(YangModelCmHandleRetriever)
 
     def noTimestamp = null
+    def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
-    def 'Register or re-register a DMI Plugin for the given cm-handle(s) with #scenario process.'() {
-        given: 'a registration'
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
-            ncmpServiceCmHandle.cmHandleID = '123'
-            ncmpServiceCmHandle.dmiProperties = [dmiProp1: 'dmiValue1', dmiProp2: 'dmiValue2']
-            ncmpServiceCmHandle.publicProperties = [publicProp1: 'publicValue1', publicProp2: 'publicValue2' ]
-            dmiPluginRegistration.createdCmHandles = createdCmHandles
-            dmiPluginRegistration.updatedCmHandles = updatedCmHandles
-            dmiPluginRegistration.removedCmHandles = removedCmHandles
-            def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","dmi-data-service-name":null,"dmi-model-service-name":null,' +
-                '"additional-properties":[{"name":"dmiProp1","value":"dmiValue1"},{"name":"dmiProp2","value":"dmiValue2"}],' +
-                '"public-properties":[{"name":"publicProp1","value":"publicValue1"},{"name":"publicProp2","value":"publicValue2"}]' +
-                '}]}'
-        when: 'registration is updated and modules are synced'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'save list elements is invoked with the expected parameters'
-            expectedCallsToSaveNode * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
-                '/dmi-registry', expectedJsonData, noTimestamp)
-        and: 'update data node leaves is called with correct parameters'
-            expectedCallsToUpdateCmHandleProperty * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(updatedCmHandles)
-        and: 'delete schema set is invoked with the correct parameters'
-            expectedCallsToDeleteSchemaSetAndListElement * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'cmHandle001', CASCADE_DELETE_ALLOWED)
-        and: 'delete list or list element is invoked with the correct parameters'
-            expectedCallsToDeleteSchemaSetAndListElement * mockCpsDataService.deleteListOrListElement('NCMP-Admin',
-                    'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
-        where:
-            scenario                    | createdCmHandles      | updatedCmHandles      | removedCmHandles || expectedCallsToSaveNode | expectedCallsToDeleteSchemaSetAndListElement | expectedCallsToUpdateCmHandleProperty
-            'create'                    | [ncmpServiceCmHandle] | []                    | []               || 1                       | 0                                            | 0
-            'update'                    | []                    | [ncmpServiceCmHandle] | []               || 0                       | 0                                            | 1
-            'delete'                    | []                    | []                    | cmHandlesArray   || 0                       | 1                                            | 0
-            'create, update and delete' | [ncmpServiceCmHandle] | [ncmpServiceCmHandle] | cmHandlesArray   || 1                       | 1                                            | 1
-            'no valid data'             | []                    | []                    | []               || 0                       | 0                                            | 0
+    def 'DMI Registration: Create, Update & Delete operations are processed in the right order'() {
+        given: 'a registration with operations of all three types'
+            def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
+            dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+            dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+            dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
+        when: 'registration is processed'
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
+            // Spock validated invocation order between multiple then blocks
+        then: 'cm-handles are removed first'
+            1 * objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_)
+        then: 'cm-handles are created'
+            1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_)
+        then: 'cm-handles are updated'
+            1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_)
     }
 
-    def 'Register a DMI Plugin for the given cm-handle(s) without DMI properties.'() {
-        given: 'a registration without cm-handle properties'
-            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
-            ncmpServiceCmHandle.cmHandleID = '123'
-            ncmpServiceCmHandle.dmiProperties = Collections.emptyMap()
-            ncmpServiceCmHandle.publicProperties = Collections.emptyMap()
-            dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
-            def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","dmi-data-service-name":null,"dmi-model-service-name":null,"additional-properties":[],"public-properties":[]}]}'
-        when: 'registration is updated'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'save list elements is invoked with the expected parameters'
-            1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
-                    '/dmi-registry', expectedJsonData, noTimestamp)
-    }
+    def 'DMI Registration: Response from all operations types are in response'() {
+        given: 'a registration with operations of all three types'
+            def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
+            dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+            dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+            dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
+        and: 'update cm-handles can be processed successfully'
+            def updateResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-2')]
+            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses
+        and: 'create cm-handles can be processed successfully'
+            def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')]
+            objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_) >> createdResponses
+        and: 'delete cm-handles can be processed successfully'
+            def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')]
+            objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_) >> removeResponses
+        when: 'registration is processed'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
+        then: 'response has values from all operations'
+            response.getRemovedCmHandles() == removeResponses
+            response.getCreatedCmHandles() == createdResponses
+            response.getUpdatedCmHandles() == updateResponses
 
-    def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during process.'() {
-        given: 'a registration without cm-handle properties '
-            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
-            dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
-        and: 'an json processing exception occurs'
-            spiedJsonObjectMapper.asJsonString(_) >> { throw (new JsonProcessingException('')) }
-        when: 'registration is updated and modules are synced'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'a data validation exception is thrown'
-            thrown(DataValidationException)
-    }
 
-    def 'Register a DMI Plugin for the given cm-handle(s) with no data found during delete process.'() {
-        given: 'a registration without cm-handle properties '
-            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
-            dmiPluginRegistration.removedCmHandles = ['some cm handle']
-        and: 'an json processing exception occurs during delete process'
-            mockCpsDataService.deleteListOrListElement(*_) >>  { throw (new DataNodeNotFoundException('','')) }
-        when: 'registration is updated and modules are synced'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'no exception is thrown'
-            noExceptionThrown()
     }
 
-    def 'Register a DMI Plugin for the given cm-handle(s) with no schema set found during delete process.'() {
-        given: 'a registration'
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
-            dmiPluginRegistration.removedCmHandles = cmHandlesArray
-        and: 'an exception occurs during delete schema set process'
-            mockCpsModuleService.deleteSchemaSet(_,_,_) >>  { throw (new Exception('')) }
-        when: 'registration is updated and modules are synced'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'delete list or list element is still called'
-            1 * mockCpsDataService.deleteListOrListElement(_,_,_,_)
-    }
-
-    def 'Dmi plugin registration with #scenario'() {
+    def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() {
         given: 'a registration '
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:dmiPlugin, dmiModelPlugin:dmiModelPlugin,
-                    dmiDataPlugin:dmiDataPlugin)
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
+                dmiDataPlugin: dmiDataPlugin)
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
         when: 'update registration and sync module is called with correct DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
@@ -165,11 +124,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'data & model using same service' | ''         | 'service1'     | 'service1'
     }
 
-    def 'Invalid DMI plugin registration with #scenario'() {
+    def 'Create CM-handle Validation: Invalid DMI plugin service name with #scenario'() {
         given: 'a registration '
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:dmiPlugin, dmiModelPlugin:dmiModelPlugin,
-                    dmiDataPlugin:dmiDataPlugin)
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
+                dmiDataPlugin: dmiDataPlugin)
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
         when: 'registration is called with incorrect DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
@@ -179,37 +137,251 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         and: 'registration is not called'
             0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
         where:
-            scenario                        | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
-            'empty DMI plugins'             | ''         | ''             | ''            || 'No DMI plugin service names'
-            'blank DMI plugins'             | ' '        | ' '            | ' '           || 'No DMI plugin service names'
-            'null DMI plugins'              | null       | null           | null          || 'No DMI plugin service names'
-            'all DMI plugins'               | 'service1' | 'service2'     | 'service3'    || 'Cannot register combined plugin service name and other service names'
-            '(combined)DMI and Data Plugin' | 'service1' | ''             | 'service2'    || 'Cannot register combined plugin service name and other service names'
-            '(combined)DMI and model Plugin'| 'service1' | 'service2'     | ''            || 'Cannot register combined plugin service name and other service names'
-            'only model DMI plugin'         | ''         | 'service1'     | ''            || 'Cannot register just a Data or Model plugin service name'
-            'only data DMI plugin'          | ''         | ''             | 'service1'    || 'Cannot register just a Data or Model plugin service name'
+            scenario                         | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
+            'empty DMI plugins'              | ''         | ''             | ''            || 'No DMI plugin service names'
+            'blank DMI plugins'              | ' '        | ' '            | ' '           || 'No DMI plugin service names'
+            'null DMI plugins'               | null       | null           | null          || 'No DMI plugin service names'
+            'all DMI plugins'                | 'service1' | 'service2'     | 'service3'    || 'Cannot register combined plugin service name and other service names'
+            '(combined)DMI and Data Plugin'  | 'service1' | ''             | 'service2'    || 'Cannot register combined plugin service name and other service names'
+            '(combined)DMI and model Plugin' | 'service1' | 'service2'     | ''            || 'Cannot register combined plugin service name and other service names'
+            'only model DMI plugin'          | ''         | 'service1'     | ''            || 'Cannot register just a Data or Model plugin service name'
+            'only data DMI plugin'           | ''         | ''             | 'service1'    || 'Cannot register just a Data or Model plugin service name'
+    }
+
+    def 'Create CM-Handle Successfully: #scenario.'() {
+        given: 'a registration without cm-handle properties'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
+            dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'cmhandle', dmiProperties: dmiProperties, publicProperties: publicProperties)]
+        when: 'registration is updated'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a successful response is received'
+            response.getCreatedCmHandles().size() == 1
+            with(response.getCreatedCmHandles().get(0)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == 'cmhandle'
+            }
+        and: 'save list elements is invoked with the expected parameters'
+            interaction {
+                def expectedJsonData = """{"cm-handles":[{"id":"cmhandle","dmi-service-name":"my-server","additional-properties":$expectedDmiProperties,"public-properties":$expectedPublicProperties}]}"""
+                1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
+                    '/dmi-registry', expectedJsonData, noTimestamp)
+            }
+        then: 'model sync is invoked with expected parameters'
+            1 * objectUnderTest.syncModulesAndCreateAnchor(_) >> { YangModelCmHandle yangModelCmHandle ->
+                {
+                    assert yangModelCmHandle.id == 'cmhandle'
+                    assert yangModelCmHandle.dmiServiceName == 'my-server'
+                    assert spiedJsonObjectMapper.asJsonString(yangModelCmHandle.getPublicProperties()) == expectedPublicProperties
+                    assert spiedJsonObjectMapper.asJsonString(yangModelCmHandle.getDmiProperties()) == expectedDmiProperties
+
+                }
+            }
+        where:
+            scenario                          | dmiProperties            | publicProperties               || expectedDmiProperties                      | expectedPublicProperties
+            'with dmi & public properties'    | ['dmi-key': 'dmi-value'] | ['public-key': 'public-value'] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[{"name":"public-key","value":"public-value"}]'
+            'with only public properties'     | [:]                      | ['public-key': 'public-value'] || '[]'                                       | '[{"name":"public-key","value":"public-value"}]'
+            'with only dmi properties'        | ['dmi-key': 'dmi-value'] | [:]                            || '[{"name":"dmi-key","value":"dmi-value"}]' | '[]'
+            'without dmi & public properties' | [:]                      | [:]                            || '[]'                                       | '[]'
+
     }
 
-    def 'Exception thrown on CM-Handle registration update request'() {
-        given: 'a CM-handle registration'
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-        and: 'dmi plugin registration input update request'
-            def dmiPluginReg = new DmiPluginRegistration();
-            dmiPluginReg.dmiPlugin = 'onap.dmap.plugin';
-            dmiPluginReg.updatedCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'unknownHandle')]
-        and: 'update data node leaves is unable to find data node'
-            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') }
-        when: 'update dmi registration is called'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginReg)
-        then: 'data validation exception is thrown'
-            def exceptionThrown = thrown(DataValidationException.class)
-            assert exceptionThrown.getDetails().contains('DataNode not found')
+    def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed'() {
+        given: 'a registration with three cm-handles to be created'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                createdCmHandles: [new NcmpServiceCmHandle(cmHandleID: 'cmhandle1'),
+                                   new NcmpServiceCmHandle(cmHandleID: 'cmhandle2'),
+                                   new NcmpServiceCmHandle(cmHandleID: 'cmhandle3')])
+        and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd'
+            mockCpsDataService.saveListElements(_, _, _, _, _) >> {} >> { throw new RuntimeException("Failed") } >> {}
+        when: 'registration is updated to create cm-handles'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a response is received for all cm-handles'
+            response.getCreatedCmHandles().size() == 3
+        and: '1st and 3rd cm-handle are created successfully'
+            with(response.getCreatedCmHandles().get(0)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == 'cmhandle1'
+            }
+            with(response.getCreatedCmHandles().get(2)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == 'cmhandle3'
+            }
+        and: '2nd cm-handle creation fails'
+            with(response.getCreatedCmHandles().get(1)) {
+                assert it.status == Status.FAILURE
+                assert it.registrationError == UNKNOWN_ERROR
+                assert it.errorText == 'Failed'
+                assert it.cmHandle == 'cmhandle2'
+            }
+    }
+
+    def 'Create CM-Handle Error Handling: Registration fails: #scenario'() {
+        given: 'a registration without cm-handle properties'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
+            dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'cmhandle')]
+        and: 'cm-handler registration fails: #scenario'
+            mockCpsDataService.saveListElements(_, _, _, _, _) >> { throw exception }
+        when: 'registration is updated'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a failure response is received'
+            response.getCreatedCmHandles().size() == 1
+            with(response.getCreatedCmHandles().get(0)) {
+                assert it.status == Status.FAILURE
+                assert it.cmHandle == 'cmhandle'
+                assert it.registrationError == expectedError
+                assert it.errorText == expectedErrorText
+            }
+        and: 'model-sync is not invoked'
+            0 * objectUnderTest.syncModulesAndCreateAnchor(_)
+        where:
+            scenario                                        | exception                                               || expectedError           | expectedErrorText
+            'cm-handle already exist'                       | new AlreadyDefinedException('', new RuntimeException()) || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists'
+            'unknown exception while registering cm-handle' | new RuntimeException('Failed')                          || UNKNOWN_ERROR           | 'Failed'
+    }
+
+    def 'Create CM-Handle Error Handling: Model Sync fails'() {
+        given: 'objects under test without disabled model sync'
+            def objectUnderTest = getObjectUnderTest()
+        and: 'a registration without cm-handle properties'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
+            dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'cmhandle')]
+        and: 'cm-handler models sync fails'
+            objectUnderTest.syncModulesAndCreateAnchor(*_) >> { throw new RuntimeException('Model-Sync failed') }
+        when: 'registration is updated'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a failure response is received'
+            response.getCreatedCmHandles().size() == 1
+            with(response.getCreatedCmHandles().get(0)) {
+                assert it.status == Status.FAILURE
+                assert it.cmHandle == 'cmhandle'
+                assert it.registrationError == UNKNOWN_ERROR
+                assert it.errorText == 'Model-Sync failed'
+            }
+        and: 'cm-handle is registered'
+            1 * mockCpsDataService.saveListElements(*_)
+    }
+
+    def 'Update CM-Handle: Update Operation Response is added to the response'() {
+        given: 'a registration to update CmHandles'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                updatedCmHandles: [{}])
+        and: 'cm-handle updates can be processed successfully'
+            def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'),
+                                           CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")),
+                                           CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLE_DOES_NOT_EXIST)]
+            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse
+        when: 'registration is updated'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'the response contains updateOperationResponse'
+            assert response.getUpdatedCmHandles().size() == 3
+            assert response.getUpdatedCmHandles().containsAll(updateOperationResponse)
+    }
+
+    def 'Remove CmHandle Successfully: #scenario'() {
+        given: 'a registration'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle'])
+        and: '#scenario'
+            mockCpsModuleService.deleteSchemaSet(_, 'cmhandle', CASCADE_DELETE_ALLOWED) >>
+                { if (!schemaSetExist) { throw new SchemaSetNotFoundException("", "") } }
+        when: 'registration is updated to delete cmhandle'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'delete list or list element is called'
+            1 * mockCpsDataService.deleteListOrListElement(_, _, _, _)
+        and: 'successful response is received'
+            assert response.getRemovedCmHandles().size() == 1
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == 'cmhandle'
+            }
+        where:
+            scenario                                            | schemaSetExist
+            'schema-set exists and can be deleted successfully' | true
+            'schema-set does not exist'                         | false
+    }
+
+    def 'Remove CmHandle: All cm-handles delete requests are processed'() {
+        given: 'a registration with three cm-handles to be deleted'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3'])
+        and: 'cm-handle deletion is successful for 1st and 3rd; failed for 2nd'
+            mockCpsDataService.deleteListOrListElement(_, _, _, _) >> {} >> { throw new RuntimeException("Failed") } >> {}
+        when: 'registration is updated to delete cmhandles'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a response is received for all cm-handles'
+            response.getRemovedCmHandles().size() == 3
+        and: '1st and 3rd cm-handle deletes successfully'
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == 'cmhandle1'
+            }
+            with(response.getRemovedCmHandles().get(2)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == 'cmhandle3'
+            }
+        and: '2nd cm-handle deletion fails'
+            with(response.getRemovedCmHandles().get(1)) {
+                assert it.status == Status.FAILURE
+                assert it.registrationError == UNKNOWN_ERROR
+                assert it.errorText == 'Failed'
+                assert it.cmHandle == 'cmhandle2'
+            }
+    }
+
+    def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() {
+        given: 'a registration'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle'])
+        and: 'schema set deletion failed with unknown error'
+            mockCpsModuleService.deleteSchemaSet(_, _, _) >> { throw new RuntimeException('Failed') }
+        when: 'registration is updated to delete cmhandle'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'no exception is thrown'
+            noExceptionThrown()
+        and: 'cm-handle is not deleted'
+            0 * mockCpsDataService.deleteListOrListElement(_, _, _, _)
+        and: 'a failure response is received'
+            assert response.getRemovedCmHandles().size() == 1
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == Status.FAILURE
+                assert it.cmHandle == 'cmhandle'
+                assert it.errorText == 'Failed'
+                assert it.registrationError == UNKNOWN_ERROR
+            }
+    }
+
+    def 'Remove CmHandle Error Handling: #scenario'() {
+        given: 'a registration'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle'])
+        and: 'cm-handle deletion throws exception'
+            mockCpsDataService.deleteListOrListElement(_, _, _, _) >> { throw deleteListElementException }
+        when: 'registration is updated to delete cmhandle'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'no exception is thrown'
+            noExceptionThrown()
+        and: 'a failure response is received'
+            assert response.getRemovedCmHandles().size() == 1
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == Status.FAILURE
+                assert it.cmHandle == 'cmhandle'
+                assert it.registrationError == expectedError
+                assert it.errorText == expectedErrorText
+            }
+        where:
+            scenario                   | deleteListElementException                | expectedError            | expectedErrorText
+            'cm-handle does not exist' | new DataNodeNotFoundException("", "", "") | CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist'
+            'an unexpected exception'  | new RuntimeException("Failed")            | UNKNOWN_ERROR            | 'Failed'
     }
 
     def getObjectUnderTestWithModelSyncDisabled() {
-        def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations,
-                mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler,mockYangModelCmHandleRetriever))
+        def objectUnderTest = getObjectUnderTest()
         objectUnderTest.syncModulesAndCreateAnchor(*_) >> null
         return objectUnderTest
     }
+
+    def getObjectUnderTest() {
+        return Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations,
+            mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler, mockYangModelCmHandleRetriever))
+    }
 }
index 9b8d4ad..f6264f4 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  * Copyright (C) 2022 Nordix Foundation
+ * Modifications Copyright (C) 2022 Bell Canada
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 package org.onap.cps.ncmp.api.impl
 
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
+
 import org.onap.cps.api.CpsDataService
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
-import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
 import spock.lang.Specification
@@ -117,12 +122,53 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
         given: 'cm handles request'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: [:], dmiProperties: [:])]
         and: 'data node cannot be found'
-            mockCpsDataService.getDataNode(*_) >> { throw new DataNodeNotFoundException(dataspaceName, anchorName, cmHandleXpath) }
+            mockCpsDataService.getDataNode(*_) >> { throw exception }
         when: 'update data node leaves is called using correct parameters'
-            objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
-        then: 'data validation exception is thrown'
-            def exceptionThrown = thrown(DataValidationException.class)
-            assert exceptionThrown.getMessage().contains('DataNode not found')
+            def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
+        then: 'one failed registration response'
+            response.size() == 1
+        and: 'it has expected error details'
+            with(response.get(0)) {
+                assert it.status == Status.FAILURE
+                assert it.cmHandle == cmHandleId
+                assert it.registrationError == expectedError
+                assert it.errorText == expectedErrorText
+            }
+        where:
+            scenario                  | exception                                                        || expectedError            | expectedErrorText
+            'cmhandle does not exist' | new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') || CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist'
+            'unexpected error'        | new RuntimeException('Failed')                                   || UNKNOWN_ERROR            | 'Failed'
+    }
+
+    def 'Multiple update operations in a single request'() {
+        given: 'cm handles request'
+            def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
+                                         new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
+                                         new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])]
+        and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle'
+            mockCpsDataService.getDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode
+        when: 'update data node leaves is called using correct parameters'
+            def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
+        then: 'response has 3 values'
+            cmHandleResponseList.size() == 3
+        and: 'the 1st and 3rd requests were processed successfully'
+            with(cmHandleResponseList.get(0)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == cmHandleId
+            }
+            with(cmHandleResponseList.get(2)) {
+                assert it.status == Status.SUCCESS
+                assert it.cmHandle == cmHandleId
+            }
+        and: 'the 2nd request failed with correct error code'
+            with(cmHandleResponseList.get(1)) {
+                assert it.status == Status.FAILURE
+                assert it.cmHandle == cmHandleId
+                assert it.registrationError == CM_HANDLE_DOES_NOT_EXIST
+                assert it.errorText == "cm-handle does not exist"
+            }
+        then: 'the replace list method is called twice'
+            2 * mockCpsDataService.replaceListContent(*_)
     }
 
     def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
@@ -133,4 +179,5 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
             }))
         return properties
     }
+
 }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
new file mode 100644 (file)
index 0000000..c5ef2f4
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Bell Canada
+ *  ================================================================================
+ *  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 org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
+import spock.lang.Specification
+
+class CmHandleRegistrationResponseSpec extends Specification {
+
+    def 'Successful CmHandle Registration Response'() {
+        when: 'CMHandle response is created'
+            def cmHandleRegistrationResponse = CmHandleRegistrationResponse.createSuccessResponse('cmHandle')
+        then: 'a success response is returned'
+            with(cmHandleRegistrationResponse) {
+                assert it.cmHandle == 'cmHandle'
+                assert it.status == Status.SUCCESS
+            }
+        and: 'error details are null'
+            cmHandleRegistrationResponse.registrationError == null
+            cmHandleRegistrationResponse.errorText == null
+    }
+
+    def 'Failed Cm Handle Registration Response: for unexpected exception'() {
+        when: 'CMHandle response is created for an unexpected exception'
+            def cmHandleRegistrationResponse =
+                CmHandleRegistrationResponse.createFailureResponse('cmHandle', new Exception('unexpected error'))
+        then: 'the response is created with expected value'
+            with(cmHandleRegistrationResponse) {
+                assert it.registrationError == RegistrationError.UNKNOWN_ERROR
+                assert it.cmHandle == 'cmHandle'
+                assert errorText == 'unexpected error'
+            }
+    }
+
+    def 'Failed Cm Handle Registration Response: for known error'() {
+        when: 'CMHandle response is created for known error'
+            def cmHandleRegistrationResponse =
+                CmHandleRegistrationResponse.createFailureResponse('cmHandle', RegistrationError.CM_HANDLE_ALREADY_EXIST)
+        then: 'the response is created with expected value'
+            with(cmHandleRegistrationResponse) {
+                assert it.registrationError == RegistrationError.CM_HANDLE_ALREADY_EXIST
+                assert it.cmHandle == 'cmHandle'
+                assert it.status == Status.FAILURE
+                assert errorText == RegistrationError.CM_HANDLE_ALREADY_EXIST.errorText
+            }
+
+    }
+
+}
index e03dce3..b76c63c 100755 (executable)
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
                 <plugin>
                     <groupId>org.springframework.boot</groupId>
                     <artifactId>spring-boot-maven-plugin</artifactId>
-                    <version>2.3.3.RELEASE</version>
+                    <version>2.6.4</version>
                     <executions>
                         <execution>
                             <goals>
index c8b88e8..514784c 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 5a21957..6019197 100755 (executable)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 6e92894..98a392a 100644 (file)
@@ -26,7 +26,7 @@
     <parent>\r
         <groupId>org.onap.cps</groupId>\r
         <artifactId>cps-parent</artifactId>\r
-        <version>3.0.0-SNAPSHOT</version>\r
+        <version>3.1.0-SNAPSHOT</version>\r
         <relativePath>../cps-parent/pom.xml</relativePath>\r
     </parent>\r
 \r
index 78862d7..bb3c2d0 100644 (file)
@@ -56,6 +56,7 @@ import org.onap.cps.spi.model.DataNodeBuilder;
 import org.onap.cps.spi.repository.AnchorRepository;
 import org.onap.cps.spi.repository.DataspaceRepository;
 import org.onap.cps.spi.repository.FragmentRepository;
+import org.onap.cps.spi.utils.SessionManager;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.stereotype.Service;
@@ -73,6 +74,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     private final JsonObjectMapper jsonObjectMapper;
 
+    private final SessionManager sessionManager;
+
     private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
     private static final Pattern REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE =
             Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$");
@@ -199,6 +202,16 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             .collect(Collectors.toUnmodifiableList());
     }
 
+    @Override
+    public String startSession() {
+        return sessionManager.startSession();
+    }
+
+    @Override
+    public void closeSession(final String sessionId) {
+        sessionManager.closeSession(sessionId);
+    }
+
     private static Set<String> processAncestorXpath(final List<FragmentEntity> fragmentEntities,
         final CpsPathQuery cpsPathQuery) {
         final Set<String> ancestorXpath = new HashSet<>();
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java b/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java
new file mode 100644 (file)
index 0000000..eb535ec
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021-2022 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.spi.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.SessionException;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+import org.onap.cps.spi.entities.AnchorEntity;
+import org.onap.cps.spi.entities.DataspaceEntity;
+import org.onap.cps.spi.entities.SchemaSetEntity;
+import org.onap.cps.spi.entities.YangResourceEntity;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SessionManager {
+
+    private static SessionFactory sessionFactory;
+    private static Map<String, Session> sessionMap = new HashMap<>();
+
+    private synchronized void buildSessionFactory() {
+        if (sessionFactory == null) {
+            sessionFactory = new Configuration().configure("hibernate.cfg.xml")
+                    .addAnnotatedClass(AnchorEntity.class)
+                    .addAnnotatedClass(DataspaceEntity.class)
+                    .addAnnotatedClass(SchemaSetEntity.class)
+                    .addAnnotatedClass(YangResourceEntity.class)
+                    .buildSessionFactory();
+        }
+    }
+
+    /**
+     * Starts a session which allows use of locks and batch interaction with the persistence service.
+     *
+     * @return Session ID string
+     */
+    public String startSession() {
+        buildSessionFactory();
+        final Session session = sessionFactory.openSession();
+        final String sessionId = UUID.randomUUID().toString();
+        sessionMap.put(sessionId, session);
+        session.beginTransaction();
+        return sessionId;
+    }
+
+    /**
+     * Close session.
+     *
+     * @param sessionId session ID
+     */
+    public void closeSession(final String sessionId) {
+        try {
+            final Session currentSession = sessionMap.get(sessionId);
+            currentSession.getTransaction().commit();
+            currentSession.close();
+        } catch (final NullPointerException e) {
+            throw new SessionException(String.format("Session with session ID %s does not exist", sessionId));
+        } catch (final HibernateException e) {
+            throw new SessionException(String.format("Unable to close session with session ID %s", sessionId));
+        }
+        sessionMap.remove(sessionId);
+    }
+
+}
\ No newline at end of file
diff --git a/cps-ri/src/main/resources/hibernate.cfg.xml b/cps-ri/src/main/resources/hibernate.cfg.xml
new file mode 100644 (file)
index 0000000..98e6cfc
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-configuration PUBLIC
+        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+    <session-factory>
+        <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
+        <property name="hibernate.connection.url">jdbc:postgresql://${DB_HOST}:${DB_PORT:5432}/cpsdb</property>
+        <property name="hibernate.connection.username">${DB_USERNAME}</property>
+        <property name="hibernate.connection.password">${DB_PASSWORD}</property>
+        <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>
+        <property name="show_sql">true</property>
+        <property name="hibernate.hbm2ddl.auto">update</property>
+    </session-factory>
+</hibernate-configuration>
\ No newline at end of file
index 7166008..c508762 100644 (file)
@@ -28,19 +28,20 @@ import org.onap.cps.spi.model.DataNodeBuilder
 import org.onap.cps.spi.repository.AnchorRepository
 import org.onap.cps.spi.repository.DataspaceRepository
 import org.onap.cps.spi.repository.FragmentRepository
+import org.onap.cps.spi.utils.SessionManager
 import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Specification
 
-
 class CpsDataPersistenceServiceSpec extends Specification {
 
     def mockDataspaceRepository = Mock(DataspaceRepository)
     def mockAnchorRepository = Mock(AnchorRepository)
     def mockFragmentRepository = Mock(FragmentRepository)
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+    def mockSessionManager = Mock(SessionManager)
 
     def objectUnderTest = new CpsDataPersistenceServiceImpl(
-            mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper)
+            mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper,mockSessionManager)
 
     def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() {
 
@@ -49,67 +50,82 @@ class CpsDataPersistenceServiceSpec extends Specification {
         def myAnchorName = 'my-anchor'
 
         given: 'data node object'
-            def submittedDataNode = new DataNodeBuilder()
-                    .withXpath(parentXpath)
-                    .withLeaves(['leaf-name': 'leaf-value'])
-                    .build()
+        def submittedDataNode = new DataNodeBuilder()
+                .withXpath(parentXpath)
+                .withLeaves(['leaf-name': 'leaf-value'])
+                .build()
         and: 'fragment to be updated'
-            mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
-                def fragmentEntity = new FragmentEntity()
-                fragmentEntity.setXpath(parentXpath)
-                fragmentEntity.setChildFragments(Collections.emptySet())
-                return fragmentEntity
-            }
+        mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
+            def fragmentEntity = new FragmentEntity()
+            fragmentEntity.setXpath(parentXpath)
+            fragmentEntity.setChildFragments(Collections.emptySet())
+            return fragmentEntity
+        }
         and: 'data node is concurrently updated by another transaction'
-            mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
+        mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
 
         when: 'attempt to update data node'
-            objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
+        objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
 
         then: 'concurrency exception is thrown'
-            def concurrencyException = thrown(ConcurrencyException)
-            assert concurrencyException.getDetails().contains(myDataspaceName)
-            assert concurrencyException.getDetails().contains(myAnchorName)
-            assert concurrencyException.getDetails().contains(parentXpath)
+        def concurrencyException = thrown(ConcurrencyException)
+        assert concurrencyException.getDetails().contains(myDataspaceName)
+        assert concurrencyException.getDetails().contains(myAnchorName)
+        assert concurrencyException.getDetails().contains(parentXpath)
     }
 
     def 'Retrieving a data node with a property JSON value of #scenario'() {
         given: 'a fragment with a property JSON value of #scenario'
-            mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
-                new FragmentEntity(childFragments: Collections.emptySet(),
-                        attributes: "{\"some attribute\": ${dataString}}")
-            }
+        mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
+            new FragmentEntity(childFragments: Collections.emptySet(),
+                    attributes: "{\"some attribute\": ${dataString}}")
+        }
         when: 'getting the data node represented by this fragment'
-            def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
-                    'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+        def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
+                'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
         then: 'the leaf is of the correct value and data type'
-            def attributeValue = dataNode.leaves.get('some attribute')
-            assert attributeValue == expectedValue
-            assert attributeValue.class == expectedDataClass
+        def attributeValue = dataNode.leaves.get('some attribute')
+        assert attributeValue == expectedValue
+        assert attributeValue.class == expectedDataClass
         where: 'the following Data Type is passed'
-            scenario                              | dataString            || expectedValue     | expectedDataClass
-            'just numbers'                        | '15174'               || 15174             | Integer
-            'number with dot'                     | '15174.32'            || 15174.32          | Double
-            'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
-            'number with 0 value before dot'      | '0.32'                || 0.32              | Double
-            'number higher than max int'          | '2147483648'          || 2147483648        | Long
-            'just text'                           | '"Test"'              || 'Test'            | String
-            'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
-            'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
-            'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
-            'number as String'                    | '"12345"'             || '12345'           | String
+        scenario                              | dataString            || expectedValue     | expectedDataClass
+        'just numbers'                        | '15174'               || 15174             | Integer
+        'number with dot'                     | '15174.32'            || 15174.32          | Double
+        'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
+        'number with 0 value before dot'      | '0.32'                || 0.32              | Double
+        'number higher than max int'          | '2147483648'          || 2147483648        | Long
+        'just text'                           | '"Test"'              || 'Test'            | String
+        'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
+        'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
+        'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
+        'number as String'                    | '"12345"'             || '12345'           | String
     }
 
     def 'Retrieving a data node with invalid JSON'() {
         given: 'a fragment with invalid JSON'
-            mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
-                new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json')
-            }
+        mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
+            new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json')
+        }
         when: 'getting the data node represented by this fragment'
-            def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
+        def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
                 'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
         then: 'a data validation exception is thrown'
-            thrown(DataValidationException)
+        thrown(DataValidationException)
     }
 
-}
+    def 'start session'() {
+        when: 'start session'
+            objectUnderTest.startSession()
+        then: 'the session manager method to start session is invoked'
+            1 * mockSessionManager.startSession()
+    }
+
+    def 'close session'() {
+        given: 'session ID'
+            def someSessionId = 'someSessionId'
+        when: 'close session method is called with session ID as parameter'
+            objectUnderTest.closeSession(someSessionId)
+        then: 'the session manager method to close session is invoked with parameter'
+            1 * mockSessionManager.closeSession(someSessionId)
+    }
+}
\ No newline at end of file
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy
new file mode 100644 (file)
index 0000000..c46092f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021-2022 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.spi.utils
+
+import org.hibernate.SessionException
+import org.onap.cps.spi.impl.CpsPersistenceSpecBase
+
+class SessionManagerIntegrationSpec extends CpsPersistenceSpecBase{
+
+    def objectUnderTest = new SessionManager();
+
+    def 'start session'() {
+        when: 'start session'
+            def result = objectUnderTest.startSession()
+        then: 'session ID is returned'
+            assert result instanceof String
+            objectUnderTest.closeSession(result)
+    }
+
+    def 'close session'(){
+        given: 'session Id from calling the start session method'
+            def sessionId = objectUnderTest.startSession()
+        when: 'close session method is called'
+            objectUnderTest.closeSession(sessionId)
+        then: 'no exception is thrown'
+            noExceptionThrown()
+    }
+
+    def 'close session that does not exist' (){
+        given: 'session Id that does not exist'
+            def unknownSessionId = 'unknown session id'
+        when: 'close session method is called'
+            objectUnderTest.closeSession(unknownSessionId)
+        then: 'a session exception is thrown'
+            def thrown = thrown(SessionException)
+            assert thrown.message.contains(unknownSessionId)
+    }
+}
diff --git a/cps-ri/src/test/resources/hibernate.cfg.xml b/cps-ri/src/test/resources/hibernate.cfg.xml
new file mode 100644 (file)
index 0000000..fae9275
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE hibernate-configuration PUBLIC\r
+        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"\r
+        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">\r
+\r
+<hibernate-configuration>\r
+    <session-factory>\r
+        <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>\r
+        <property name="hibernate.connection.url">${DB_URL}</property>\r
+        <property name="hibernate.connection.username">${DB_USERNAME}</property>\r
+        <property name="hibernate.connection.password">${DB_PASSWORD}</property>\r
+        <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>\r
+        <property name="show_sql">true</property>\r
+        <property name="hibernate.hbm2ddl.auto">none</property>\r
+    </session-factory>\r
+</hibernate-configuration>
\ No newline at end of file
index 9c7031e..aea122d 100644 (file)
@@ -28,7 +28,7 @@
   <parent>\r
     <groupId>org.onap.cps</groupId>\r
     <artifactId>cps-parent</artifactId>\r
-    <version>3.0.0-SNAPSHOT</version>\r
+    <version>3.1.0-SNAPSHOT</version>\r
     <relativePath>../cps-parent/pom.xml</relativePath>\r
   </parent>\r
 \r
index cdd417b..35caf95 100644 (file)
@@ -174,4 +174,19 @@ public interface CpsDataService {
      */
     void updateNodeLeavesAndExistingDescendantLeaves(String dataspaceName, String anchorName, String parentNodeXpath,
         String dataNodeUpdatesAsJson, OffsetDateTime observedTimestamp);
+
+    /**
+     * Starts a session which allows use of locks and batch interaction with the persistence service.
+     *
+     * @return Session ID string
+     */
+    String startSession();
+
+    /**
+     * Close session.
+     *
+     * @param sessionId session ID
+     *
+     */
+    void closeSession(String sessionId);
 }
index aae355d..643614f 100755 (executable)
@@ -108,6 +108,17 @@ public class CpsDataServiceImpl implements CpsDataService {
         processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE);
     }
 
+    @Override
+    public String startSession() {
+        final String sessionId = cpsDataPersistenceService.startSession();
+        return sessionId;
+    }
+
+    @Override
+    public void closeSession(final String sessionId) {
+        cpsDataPersistenceService.closeSession(sessionId);
+    }
+
     @Override
     public void replaceNodeTree(final String dataspaceName, final String anchorName, final String parentNodeXpath,
         final String jsonData, final OffsetDateTime observedTimestamp) {
index fd65886..fdcf15b 100644 (file)
@@ -148,4 +148,17 @@ public interface CpsDataPersistenceService {
     Collection<DataNode> queryDataNodes(String dataspaceName, String anchorName,
         String cpsPath, FetchDescendantsOption fetchDescendantsOption);
 
+    /**
+     * Starts a session which allows use of locks and batch interaction with the persistence service.
+     *
+     * @return Session ID string
+     */
+    String startSession();
+
+    /**
+     * Close session.
+     *
+     * @param sessionId session ID
+     */
+    void closeSession(String sessionId);
 }
index 785788b..eb06199 100644 (file)
@@ -254,4 +254,20 @@ class CpsDataServiceImplSpec extends Specification {
         def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
         mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
     }
+
+    def 'start session'() {
+        when: 'start session method is called'
+            objectUnderTest.startSession()
+        then: 'the persistence service method to start session is invoked'
+            1 * mockCpsDataPersistenceService.startSession()
+    }
+
+    def 'close session'(){
+        given: 'session Id from calling the start session method'
+            def sessionId = objectUnderTest.startSession()
+        when: 'close session method is called'
+            objectUnderTest.closeSession(sessionId)
+        then: 'the persistence service method to close session is invoked'
+            1 * mockCpsDataPersistenceService.closeSession(sessionId)
+    }
 }
index 203151b..135040f 100644 (file)
@@ -49,8 +49,6 @@ CPS Log pattern
 Change logging level
 --------------------
 
-.. container:: ulist
-
 - Curl command 1. Check current log level of "logging.level.org.onap.cps" if it is set to it's default value (INFO)
 
 .. code-block:: java
index 2fc8d7f..983252f 100644 (file)
@@ -15,27 +15,28 @@ info:
   x-logo:
     url: cps_logo.png
 servers:
-  - url: /cps/api
+- url: /cps/api
 tags:
-  - name: cps-admin
-    description: cps Admin
-  - name: cps-data
-    description: cps Data
+- name: cps-admin
+  description: cps Admin
+- name: cps-data
+  description: cps Data
 paths:
   /v1/dataspaces:
     post:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Create a dataspace
       description: Create a new dataspace
       operationId: createDataspace
       parameters:
-        - name: dataspace-name
-          in: query
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: query
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
       responses:
         "201":
           description: Created
@@ -43,38 +44,130 @@ paths:
             text/plain:
               schema:
                 type: string
+                example: my-resource
+        "401":
+          description: Unauthorized
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
+        "403":
+          description: Forbidden
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "409":
+          description: Conflict
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 409
+                message: Conflicting request
+                details: The request cannot be processed as the resource is in use.
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
+    delete:
+      tags:
+      - cps-admin
+      summary: Delete a dataspace
+      description: Delete a dataspace
+      operationId: deleteDataspace
+      parameters:
+      - name: dataspace-name
+        in: query
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      responses:
+        "204":
+          description: No Content
+          content: {}
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "409":
+          description: Conflict
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 409
+                message: Conflicting request
+                details: The request cannot be processed as the resource is in use.
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
   /v1/dataspaces/{dataspace-name}/anchors:
     get:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Get anchors
       description: "Read all anchors, given a dataspace"
       operationId: getAnchors
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
       responses:
         "200":
           description: OK
@@ -90,49 +183,68 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
-        "404":
-          description: The specified resource was not found
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     post:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Create an anchor
       description: Create a new anchor in the given dataspace
       operationId: createAnchor
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: schema-set-name
-          in: query
-          description: schema-set-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: query
-          description: anchor-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: schema-set-name
+        in: query
+        description: schema-set-name
+        required: true
+        schema:
+          type: string
+          example: my-schema-set
+      - name: anchor-name
+        in: query
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
       responses:
         "201":
           description: Created
@@ -140,44 +252,79 @@ paths:
             text/plain:
               schema:
                 type: string
+                example: my-resource
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "409":
+          description: Conflict
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 409
+                message: Conflicting request
+                details: The request cannot be processed as the resource is in use.
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
   /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}:
     get:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Get an anchor
       description: Read an anchor given an anchor name and a dataspace
       operationId: getAnchor
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
       responses:
         "200":
           description: OK
@@ -191,43 +338,61 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
-        "404":
-          description: The specified resource was not found
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     delete:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Delete an anchor
       description: Delete an anchor given an anchor name and a dataspace
       operationId: deleteAnchor
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
       responses:
         "204":
           description: No Content
@@ -238,38 +403,62 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
   /v1/dataspaces/{dataspace-name}/schema-sets:
     post:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Create a schema set
       description: Create a new schema set in the given dataspace
       operationId: createSchemaSet
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: schema-set-name
-          in: query
-          description: schema-set-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: schema-set-name
+        in: query
+        description: schema-set-name
+        required: true
+        schema:
+          type: string
+          example: my-schema-set
       requestBody:
         content:
           multipart/form-data:
@@ -283,44 +472,79 @@ paths:
             text/plain:
               schema:
                 type: string
+                example: my-resource
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "409":
+          description: Conflict
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 409
+                message: Conflicting request
+                details: The request cannot be processed as the resource is in use.
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
   /v1/dataspaces/{dataspace-name}/schema-sets/{schema-set-name}:
     get:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Get a schema set
       description: Read a schema set given a schema set name and a dataspace
       operationId: getSchemaSet
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: schema-set-name
-          in: path
-          description: schema-set-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: schema-set-name
+        in: path
+        description: schema-set-name
+        required: true
+        schema:
+          type: string
+          example: my-schema-set
       responses:
         "200":
           description: OK
@@ -334,43 +558,61 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
-        "404":
-          description: The specified resource was not found
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     delete:
       tags:
-        - cps-admin
+      - cps-admin
       summary: Delete a schema set
       description: Delete a schema set given a schema set name and a dataspace
       operationId: deleteSchemaSet
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: schema-set-name
-          in: path
-          description: schema-set-name
-          required: true
-          schema:
-            type: string
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: schema-set-name
+        in: path
+        description: schema-set-name
+        required: true
+        schema:
+          type: string
+          example: my-schema-set
       responses:
         "204":
           description: No Content
@@ -381,59 +623,93 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
         "409":
           description: Conflict
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 409
+                message: Conflicting request
+                details: The request cannot be processed as the resource is in use.
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
   /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/node:
     get:
       tags:
-        - cps-data
+      - cps-data
       summary: Get a node
       description: Get a node with an option to retrieve all the children for a given
         anchor and dataspace
       operationId: getNodeByDataspaceAndAnchor
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: false
-          schema:
-            type: string
-            default: /
-        - name: include-descendants
-          in: query
-          description: include-descendants
-          required: false
-          schema:
-            type: boolean
-            default: false
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: false
+        schema:
+          type: string
+          default: /
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: include-descendants
+        in: query
+        description: include-descendants
+        required: false
+        schema:
+          type: boolean
+          example: false
+          default: false
       responses:
         "200":
           description: OK
@@ -441,75 +717,100 @@ paths:
             application/json:
               schema:
                 type: object
-              example:
-                child: my_child
-                leafList: "leafListElement1, leafListElement2"
-                leaf: my_leaf
+              examples:
+                dataSample:
+                  $ref: '#/components/examples/dataSample'
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
-        "404":
-          description: The specified resource was not found
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
       x-codegen-request-body-name: xpath
   /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes:
     put:
       tags:
-        - cps-data
+      - cps-data
       summary: Replace a node with descendants
       description: "Replace a node with descendants for a given dataspace, anchor\
         \ and a parent node xpath"
       operationId: replaceNode
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: false
-          schema:
-            type: string
-            default: /
-        - name: observed-timestamp
-          in: query
-          description: observed-timestamp
-          required: false
-          schema:
-            type: string
-            example: 2021-03-21T00:10:34.030-0100
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: false
+        schema:
+          type: string
+          default: /
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
       requestBody:
         content:
           application/json:
             schema:
-              type: string
+              type: object
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSample'
         required: true
       responses:
         "200":
@@ -518,64 +819,97 @@ paths:
             application/json:
               schema:
                 type: object
-              example:
-                key: value
+              examples:
+                dataSample:
+                  value: ""
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     post:
       tags:
-        - cps-data
+      - cps-data
       summary: Create a node
       description: Create a node for a given anchor and dataspace
       operationId: createNode
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: false
-          schema:
-            type: string
-            default: /
-        - name: observed-timestamp
-          in: query
-          description: observed-timestamp
-          required: false
-          schema:
-            type: string
-            example: 2021-03-21T00:10:34.030-0100
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: false
+        schema:
+          type: string
+          default: /
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
       requestBody:
         content:
           application/json:
             schema:
-              type: string
+              type: object
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSample'
         required: true
       responses:
         "201":
@@ -584,63 +918,191 @@ paths:
             text/plain:
               schema:
                 type: string
+                example: my-resource
+        "400":
+          description: Bad Request
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
+        "401":
+          description: Unauthorized
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
+        "403":
+          description: Forbidden
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "409":
+          description: Conflict
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 409
+                message: Conflicting request
+                details: The request cannot be processed as the resource is in use.
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
+    delete:
+      tags:
+      - cps-data
+      summary: Delete a data node
+      description: Delete a datanode for a given dataspace and anchor given a node
+        xpath.
+      operationId: deleteDataNode
+      parameters:
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: false
+        schema:
+          type: string
+          default: /
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
+      responses:
+        "204":
+          description: No Content
+          content: {}
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     patch:
       tags:
-        - cps-data
+      - cps-data
       summary: Update node leaves
       description: Update a data node leaves for a given dataspace and anchor and
         a parent node xpath
       operationId: updateNodeLeaves
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: false
-          schema:
-            type: string
-            default: /
-        - name: observed-timestamp
-          in: query
-          description: observed-timestamp
-          required: false
-          schema:
-            type: string
-            example: 2021-03-21T00:10:34.030-0100
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: false
+        schema:
+          type: string
+          default: /
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
       requestBody:
         content:
           application/json:
             schema:
-              type: string
+              type: object
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSample'
         required: true
       responses:
         "200":
@@ -649,129 +1111,195 @@ paths:
             application/json:
               schema:
                 type: object
-              example:
-                key: value
+              examples:
+                dataSample:
+                  value: ""
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
   /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/list-nodes:
     put:
       tags:
-        - cps-data
-      summary: Replace list-node child element(s) under existing parent node
-      description: Replace list-node child elements under existing node for a given
-        anchor and dataspace
-      operationId: replaceListNodeElements
+      - cps-data
+      summary: Replace list content
+      description: "Replace list content under a given parent, anchor and dataspace"
+      operationId: replaceListContent
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: true
-          schema:
-            type: string
-        - name: observed-timestamp
-          in: query
-          description: observed-timestamp
-          required: false
-          schema:
-            type: string
-            example: 2021-03-21T00:10:34.030-0100
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: true
+        schema:
+          type: string
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
       requestBody:
         content:
           application/json:
             schema:
-              type: string
+              type: object
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSample'
         required: true
       responses:
         "200":
-          description: Created
+          description: OK
           content:
-            text/plain:
+            application/json:
               schema:
-                type: string
+                type: object
+              examples:
+                dataSample:
+                  value: ""
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     post:
       tags:
-        - cps-data
-      summary: Add list-node child element(s) under existing parent node
-      description: Add list-node child elements to existing node for a given anchor
-        and dataspace
-      operationId: addListNodeElements
+      - cps-data
+      summary: Add list element(s)
+      description: Add list element(s) to a list for a given anchor and dataspace
+      operationId: addListElements
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: true
-          schema:
-            type: string
-        - name: observed-timestamp
-          in: query
-          description: observed-timestamp
-          required: false
-          schema:
-            type: string
-            example: 2021-03-21T00:10:34.030-0100
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: true
+        schema:
+          type: string
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
       requestBody:
         content:
           application/json:
             schema:
-              type: string
+              type: object
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSample'
         required: true
       responses:
         "201":
@@ -780,57 +1308,86 @@ paths:
             text/plain:
               schema:
                 type: string
+                example: my-resource
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
     delete:
       tags:
-        - cps-data
-      summary: Delete list-node child element(s) under existing parent node
-      description: Delete list-node child elements under existing node for a given
-        anchor and dataspace
-      operationId: deleteListNodeElements
+      - cps-data
+      summary: Delete one or all list element(s)
+      description: Delete one or all list element(s) for a given anchor and dataspace
+      operationId: deleteListOrListElement
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: xpath
-          in: query
-          description: xpath
-          required: true
-          schema:
-            type: string
-        - name: observed-timestamp
-          in: query
-          description: observed-timestamp
-          required: false
-          schema:
-            type: string
-            example: 2021-03-21T00:10:34.030-0100
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: xpath
+        in: query
+        description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: true
+        schema:
+          type: string
+        examples:
+          container xpath:
+            value: /shops/bookstore
+          list attributes xpath:
+            value: "/shops/bookstore/categories[@code=1]"
+      - name: observed-timestamp
+        in: query
+        description: observed-timestamp
+        required: false
+        schema:
+          type: string
+          example: 2021-03-21T00:10:34.030-0100
       responses:
         "204":
           description: No Content
@@ -841,52 +1398,83 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
+      deprecated: true
   /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes/query:
     get:
       tags:
-        - cps-query
+      - cps-query
       summary: Query data nodes
       description: Query data nodes for the given dataspace and anchor using CPS path
       operationId: getNodesByDataspaceAndAnchorAndCpsPath
       parameters:
-        - name: dataspace-name
-          in: path
-          description: dataspace-name
-          required: true
-          schema:
-            type: string
-        - name: anchor-name
-          in: path
-          description: anchor-name
-          required: true
-          schema:
-            type: string
-        - name: cps-path
-          in: query
-          description: cps-path
-          required: false
-          schema:
-            type: string
-            default: /
-        - name: include-descendants
-          in: query
-          description: include-descendants
-          required: false
-          schema:
-            type: boolean
-            default: false
+      - name: dataspace-name
+        in: path
+        description: dataspace-name
+        required: true
+        schema:
+          type: string
+          example: my-dataspace
+      - name: anchor-name
+        in: path
+        description: anchor-name
+        required: true
+        schema:
+          type: string
+          example: my-anchor
+      - name: cps-path
+        in: query
+        description: "For more details on cps path, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
+        required: false
+        schema:
+          type: string
+          default: /
+        examples:
+          container cps path:
+            value: //bookstore
+          list attributes cps path:
+            value: "//categories[@code=1]"
+      - name: include-descendants
+        in: query
+        description: include-descendants
+        required: false
+        schema:
+          type: boolean
+          example: false
+          default: false
       responses:
         "200":
           description: OK
@@ -894,32 +1482,49 @@ paths:
             application/json:
               schema:
                 type: object
-              example:
-                key: value
+              examples:
+                dataSample:
+                  $ref: '#/components/examples/dataSample'
         "400":
           description: Bad Request
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 400
+                message: Bad Request
+                details: The provided request is not valid
         "401":
           description: Unauthorized
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 401
+                message: Unauthorized request
+                details: This request is unauthorized
         "403":
           description: Forbidden
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
-        "404":
-          description: The specified resource was not found
+              example:
+                status: 403
+                message: Request Forbidden
+                details: This request is forbidden
+        "500":
+          description: Internal Server Error
           content:
             application/json:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
       x-codegen-request-body-name: xpath
 components:
   schemas:
@@ -929,29 +1534,26 @@ components:
       properties:
         status:
           type: string
-          example: "400"
         message:
           type: string
-          example: Dataspace not found
         details:
           type: string
-          example: Dataspace with name D1 does not exist.
     AnchorDetails:
       title: Anchor details by anchor Name
       type: object
       properties:
         name:
           type: string
-          example: my_anchor
+          example: my-anchor
         dataspaceName:
           type: string
-          example: my_dataspace
+          example: my-dataspace
         schemaSetName:
           type: string
-          example: my_schema_set
+          example: my-schema-set
     MultipartFile:
       required:
-        - file
+      - file
       type: object
       properties:
         file:
@@ -960,28 +1562,40 @@ components:
           format: binary
     SchemaSetDetails:
       title: Schema set details by dataspace and schemasetName
+      required:
+      - moduleReferences
       type: object
       properties:
         dataspaceName:
           type: string
-          example: my_dataspace
+          example: my-dataspace
         moduleReferences:
           type: array
           items:
             $ref: '#/components/schemas/ModuleReferences'
         name:
           type: string
-          example: my_schema_set
+          example: my-schema-set
     ModuleReferences:
       title: Module reference object
       type: object
       properties:
         name:
           type: string
-          example: module_reference_name
+          example: my-module-reference-name
         namespace:
           type: string
-          example: module_reference_namespace
+          example: my-module-reference-namespace
         revision:
           type: string
-          example: module_reference_revision
+          example: my-module-reference-revision
+  examples:
+    dataSample:
+      value:
+        test:bookstore:
+          bookstore-name: Chapters
+          categories:
+          - code: 1
+            name: SciFi
+          - code: 2
+            name: kids
index 154a441..30896f6 100644 (file)
@@ -86,23 +86,16 @@ components:
             $ref: '#/components/schemas/RestInputCmHandle'
         updatedCmHandles:
           type: array
-          example:
-            cmHandle: my-cm-handle
-            cmHandleProperties:
-              add-my-property: add-property
-              update-my-property: updated-property
-              delete-my-property: ~
-            publicCmHandleProperties:
-              add-my-property: add-property
-              update-my-property: updated-property
-              delete-my-property: ~
           items:
             $ref: '#/components/schemas/RestInputCmHandle'
         removedCmHandles:
           type: array
+          example:
+          - my-cm-handle1
+          - my-cm-handle2
+          - my-cm-handle3
           items:
             type: string
-            example: "[\"my-cm-handle1\",\"my-cm-handle2\",\"my-cm-handle3\"]"
     RestInputCmHandle:
       required:
       - cmHandle
index b7a6563..5a6a600 100644 (file)
@@ -70,6 +70,17 @@ paths:
           sample 3:
             value:
               options: "(depth=2,fields=book/authors)"
+      - name: topic
+        in: query
+        description: topic parameter in query.
+        required: false
+        allowReserved: true
+        schema:
+          type: string
+        examples:
+          sample 1:
+            value:
+              topic: my-topic-name
       responses:
         "200":
           description: OK
@@ -184,6 +195,17 @@ paths:
           sample 3:
             value:
               options: "(depth=2,fields=book/authors)"
+      - name: topic
+        in: query
+        description: topic parameter in query.
+        required: false
+        allowReserved: true
+        schema:
+          type: string
+        examples:
+          sample 1:
+            value:
+              topic: my-topic-name
       responses:
         "200":
           description: OK
@@ -664,7 +686,7 @@ paths:
               schema:
                 type: array
                 items:
-                  $ref: '#/components/schemas/ModuleReference'
+                  $ref: '#/components/schemas/RestModuleReference'
         "400":
           description: Bad Request
           content:
@@ -851,7 +873,7 @@ components:
           type: string
         details:
           type: string
-    ModuleReference:
+    RestModuleReference:
       title: Module reference details
       type: object
       properties:
index 2f68a64..46160c4 100644 (file)
@@ -7,13 +7,13 @@
 .. _deployment:
 
 CPS Deployment
-==============
+##############
 
 .. contents::
     :depth: 2
 
 CPS OOM Charts
---------------
+==============
 The CPS kubernetes chart is located in the `OOM repository <https://github.com/onap/oom/tree/master/kubernetes/cps>`_.
 This chart includes different cps components referred as <cps-component-name> further in the document are listed below:
 
@@ -26,7 +26,8 @@ This chart includes different cps components referred as <cps-component-name> fu
 Please refer to the `OOM documentation <https://docs.onap.org/projects/onap-oom/en/latest/oom_user_guide.html>`_ on how to install and deploy ONAP.
 
 Installing or Upgrading CPS Components
---------------------------------------
+======================================
+
 The assumption is you have cloned the charts from the OOM repository into a local directory.
 
 **Step 1** Go to the cps charts and edit properties in values.yaml files to make any changes to particular cps component if required.
@@ -91,7 +92,7 @@ After deploying cps, keep monitoring the cps pods until they come up.
   kubectl get pods -n <namespace> | grep <cps-component-name>
 
 Restarting a faulty component
------------------------------
+=============================
 Each cps component can be restarted independently by issuing the following command:
 
 .. code-block:: bash
@@ -102,7 +103,7 @@ Each cps component can be restarted independently by issuing the following comma
 .. _cps_common_credentials_retrieval:
 
 Credentials Retrieval
----------------------
+=====================
 
 Application and database credentials are kept in Kubernetes secrets. They are defined as external secrets in the
 values.yaml file to be used across different components as :
@@ -161,8 +162,9 @@ Additional Cps-Core Customizations
 ==================================
 
 The following table lists some properties that can be specified as Helm chart
-values to configure the application to be deployed. This list is not
-exhaustive.
+values to configure the application to be deployed. This list is not exhaustive.
+
+Any spring supported property can be configured by providing in ``config.additional.<spring-supported-property-name>: value`` Example: config.additional.spring.datasource.hikari.maximumPoolSize: 30
 
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
 | Property                              | Description                                                                                             | Default Value                 |
@@ -277,6 +279,10 @@ exhaustive.
 | notification.async.executor.          |                                                                                                         |                               |
 | thread-name-prefix                    |                                                                                                         |                               |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional.                    | Specifies number of database connections between database and application.                              | ``10``                        |
+| spring.datasource.hikari.             | This property controls the maximum size that the pool is allowed to reach,                              |                               |
+| maximumPoolSize                       | including both idle and in-use connections.                                                             |                               |
++---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
 
 CPS-Core Docker Installation
 ============================
index 62ba5e8..eaf3646 100755 (executable)
@@ -9,7 +9,10 @@
 .. _cps-framework-doc:
 
 CPS Documentation
------------------
+#################
+
+CPS Core
+========
 
 .. toctree::
    :maxdepth: 1
@@ -22,12 +25,12 @@ CPS Documentation
    deployment.rst
    release-notes.rst
 
-DMI-Plugin Documentation
-------------------------
+DMI-Plugin
+==========
 
 * :ref:`DMI-Plugin<onap-cps-ncmp-dmi-plugin:master_index>`
 
-CPS-Temporal Documentation
---------------------------
+CPS Temporal
+============
 
 * :ref:`CPS-Temporal<onap-cps-cps-temporal:master_index>`
index 4b69dd8..cde6f6d 100644 (file)
@@ -4,7 +4,7 @@
 .. _overview:
 
 CPS Overview
-============
+############
 
 The Configuration Persistence Service (CPS) is a platform component that is designed to serve as a
 data repository for runtime data that needs persistence.
@@ -28,10 +28,10 @@ Types of data that is stored:
   configuration and operational parameters depending on how they are used.
 
 CPS Components
---------------
+==============
 
 CPS-Core
-########
+--------
 This is the component of CPS which encompasses the generic storage of Yang module data.
 
 **NCMP**
@@ -43,13 +43,13 @@ NCMP accesses all network Data-Model-Inventory (DMI) information via NCMP-DMI-Pl
 even though CPS-Core could be deployed without the NCMP extension.
 
 NCMP-DMI-Plugin
-####################
+---------------
 
 The Data-Model-Inventory (DMI) Plugin is a rest interface used to synchronize CM-Handles data between CPS and DMI through the DMI-Plugin.
 This is built previously from the CPS-NF-Proxy component.
 
 CPS-Temporal
-############
+------------
 
 This service is responsible to provide a time oriented perspective for
 operational network data. It provides features to store and retrieve sequences
@@ -57,14 +57,8 @@ of configurations or states along with the associated times when they occurred
 or have been observed.
 
 CPS Project
------------
-
-Wiki: `Configuration Persistence Service Project <https://wiki.onap.org/display/DW/Configuration+Persistence+Service+Project>`_
-
-Contact Information
--------------------
-
-onap-discuss@lists.onap.org
+===========
 
-Meeting details `Join  <https://zoom.us/j/836561560?pwd=TTZNcFhXTWYxMmZ4SlgzcVZZQXluUT09>`_
-`Agenda <https://wiki.onap.org/pages/viewpage.action?pageId=111117075>`_
+* Wiki: `Configuration Persistence Service Project <https://wiki.onap.org/display/DW/Configuration+Persistence+Service+Project>`_
+* Contact Information: onap-discuss@lists.onap.org
+* Meeting details: `Join  <https://zoom.us/j/836561560?pwd=TTZNcFhXTWYxMmZ4SlgzcVZZQXluUT09>`_ & `Agenda <https://wiki.onap.org/pages/viewpage.action?pageId=111117075>`_
index 9172e9f..2fea4a2 100755 (executable)
@@ -5,11 +5,8 @@
 .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
 .. _release_notes:
 
-
-
-=================
 CPS Release Notes
-=================
+#################
 
 .. contents::
     :depth: 2
@@ -19,10 +16,25 @@ CPS Release Notes
 ..      * * *   JAKARTA   * * *
 ..      ========================
 
-Version: 3.0.0-SNAPSHOT
-=======================
+Version: 3.0.0
+==============
+
+Release Data
+------------
 
-This section lists the main changes & fixes merged into master (snapshot) version of CPS-NCMP. This information is here to assist developers that want experiment/test using our latest code bases directly. Stability of this is not guaranteed.
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project**                      |                                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images**                    | onap/cps-and-ncmp:3.0.0                                |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation**              | 3.0.0 Jakarta                                          |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release date**                     | 2022 March 15                                          |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
 
 Features
 --------
@@ -33,6 +45,8 @@ Features
    - `CPS-741 <https://jira.onap.org/browse/CPS-741>`_  Re sync after removing cm handles
    - `CPS-777 <https://jira.onap.org/browse/CPS-777>`_  Ensure all DMI operations use POST method
    - `CPS-780 <https://jira.onap.org/browse/CPS-780>`_  Add examples for parameters, request and response in openapi yaml for cps-core
+   - `CPS-789 <https://jira.onap.org/browse/CPS-789>`_ CPS Data Updated Event Schema V2 to support delete operation
+   - `CPS-791 <https://jira.onap.org/browse/CPS-791>`_ CPS-Core sends delete notification event
    - `CPS-817 <https://jira.onap.org/browse/CPS-817>`_  Create Endpoint For Get Cm Handles (incl. public properties) By Name
    - `CPS-837 <https://jira.onap.org/browse/CPS-837>`_  Add Remove and Update properties (DMI and Public) as part of CM Handle Registration update
 
index d42d89a..d1181d3 100644 (file)
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
+        <version>3.1.0-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/pom.xml b/pom.xml
index 87398bd..23ef44b 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
 \r
     <groupId>org.onap.cps</groupId>\r
     <artifactId>cps-aggregator</artifactId>\r
-    <version>3.0.0-SNAPSHOT</version>\r
+    <version>3.1.0-SNAPSHOT</version>\r
     <packaging>pom</packaging>\r
 \r
     <name>cps</name>\r
diff --git a/releases/3.0.0-container.yaml b/releases/3.0.0-container.yaml
new file mode 100644 (file)
index 0000000..f227bdb
--- /dev/null
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.0.0
+project: cps
+log_dir: cps-maven-docker-stage-master/504/
+ref: a1129b696f3197fc7d8a3b63bcd84b5b2dd8874e
+containers:
+  - name: 'cps-and-ncmp'
+    version: '3.0.0-20220315T180237Z'
diff --git a/releases/3.0.0.yaml b/releases/3.0.0.yaml
new file mode 100644 (file)
index 0000000..60dd811
--- /dev/null
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/504/
+project: cps
+version: 3.0.0
index 50cef48..df033a3 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
         <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
     </properties>
 
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-deploy-plugin</artifactId>
+                    <version>2.8.2</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
     <distributionManagement>
         <repository>
             <id>ecomp-releases</id>
index 17f2daa..870b994 100755 (executable)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021 Nordix Foundation
+#  Copyright (C) 2021-2022 Nordix Foundation
 #  Modifications Copyright (C) 2022 Bell Canada.
 #  ================================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@
 # because they are used in Jenkins, whose plug-in doesn't support this
 
 major=3
-minor=0
+minor=1
 patch=0
 
 base_version=${major}.${minor}.${patch}