Merge "Create Preliminary Documentation for CPS-Core & NCMP"
authorNiamh Core <niamh.core@est.tech>
Tue, 14 Sep 2021 14:49:07 +0000 (14:49 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 14 Sep 2021 14:49:07 +0000 (14:49 +0000)
60 files changed:
INFO.yaml
checkstyle/pom.xml
cps-application/pom.xml
cps-application/src/main/resources/application.yml
cps-bom/pom.xml
cps-dependencies/pom.xml
cps-events/pom.xml
cps-ncmp-rest/docs/openapi/ncmproxy.yml
cps-ncmp-rest/docs/openapi/openapi.yml
cps-ncmp-rest/pom.xml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
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/JsonUtils.java [new file with mode: 0644]
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/client/DmiRestClient.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/JsonUtilsSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/PersistenceCmHandleSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy
cps-ncmp-service/src/test/resources/application.yml [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/impl/CpsModulePersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy
cps-ri/src/test/resources/data/schemaset.sql
cps-service/pom.xml
cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
cps-service/src/main/java/org/onap/cps/config/AsyncConfig.java
cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java
cps-service/src/main/java/org/onap/cps/spi/model/ExtendedModuleReference.java [new file with mode: 0644]
cps-service/src/main/java/org/onap/cps/spi/model/ModuleReference.java
cps-service/src/main/java/org/onap/cps/spi/model/SchemaSet.java
cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSet.java
cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSetBuilder.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
cps-service/src/test/resources/application.yml
pom.xml
releases/2.0.0-container.yaml [new file with mode: 0644]
releases/2.0.0.yaml [new file with mode: 0644]
spotbugs/pom.xml
version.properties

index d4308d1..4683810 100755 (executable)
--- a/INFO.yaml
+++ b/INFO.yaml
@@ -39,7 +39,7 @@ mailing_list:
 realtime_discussion: ''
 meetings:
     - type: 'zoom'
-      agenda: 'https://wiki.onap.org/pages/viewpage.action?pageId=84644224'
+      agenda: 'https://wiki.onap.org/pages/viewpage.action?pageId=111117075'
       url: 'https://zoom.us/j/836561560?pwd=TTZNcFhXTWYxMmZ4SlgzcVZZQXluUT09'
       server: 'n/a'
       channel: 'n/a'
index 3b7f3ed..2843c11 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
index 9ce0006..dfa4697 100755 (executable)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>1.1.0-SNAPSHOT</version>
+        <version>2.0.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 82c6e0a..42addf1 100644 (file)
@@ -74,12 +74,14 @@ notification:
         topic: ${CPS_CHANGE_EVENT_TOPIC:cps.data-updated-events}\r
         filters:\r
             enabled-dataspaces: ${NOTIFICATION_DATASPACE_FILTER_PATTERNS:""}\r
-    async-executor:\r
-        core-pool-size: 2\r
-        max-pool-size: 10\r
-        queue-capacity: 500\r
-        wait-for-tasks-to-complete-on-shutdown: true\r
-        thread-name-prefix: Async-\r
+    async:\r
+        enabled: false\r
+        executor:\r
+            core-pool-size: 2\r
+            max-pool-size: 10\r
+            queue-capacity: 500\r
+            wait-for-tasks-to-complete-on-shutdown: true\r
+            thread-name-prefix: Async-\r
 \r
 \r
 springdoc:\r
index 1f8789c..148b145 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
index 1cd36f6..348244d 100755 (executable)
@@ -23,7 +23,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
index 3844408..3dc5f1e 100644 (file)
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>1.1.0-SNAPSHOT</version>
+        <version>2.0.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 138337d..5e2957f 100755 (executable)
@@ -279,5 +279,32 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
+      404:
+        $ref: 'components.yaml#/components/responses/NotFound'
+
+fetchModuleReferencesByCmHandle:
+  get:
+    description: fetch all module references (name and revision) for a given cm handle
+    tags:
+      - network-cm-proxy
+    summary: Fetch all module references (name and revision) for a given cm handle
+    operationId: getModuleReferencesByCmHandle
+    parameters:
+      - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+    responses:
+      200:
+        description: OK
+        content:
+          application/json:
+            schema:
+              type: string
+            example: [{"moduleName": "nc-notifications", "revision": "2008-07-14"}]
+        $ref: 'components.yaml#/components/responses/Ok'
+      400:
+        $ref: 'components.yaml#/components/responses/BadRequest'
+      401:
+        $ref: 'components.yaml#/components/responses/Unauthorized'
+      403:
+        $ref: 'components.yaml#/components/responses/Forbidden'
       404:
         $ref: 'components.yaml#/components/responses/NotFound'
\ No newline at end of file
index 8d8684a..12356b5 100755 (executable)
@@ -45,4 +45,7 @@ paths:
     $ref: 'ncmproxy.yml#/getResourceDataForPassthroughOperational'
 
   /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running/{resourceIdentifier}:
-    $ref: 'ncmproxy.yml#/resourceDataForPassthroughRunning'
\ No newline at end of file
+    $ref: 'ncmproxy.yml#/resourceDataForPassthroughRunning'
+
+  /v1/ch/{cm-handle}/modules:
+    $ref: 'ncmproxy.yml#/fetchModuleReferencesByCmHandle'
\ No newline at end of file
index d3de688..e77ede5 100644 (file)
@@ -27,7 +27,7 @@
 <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
 </parent>
 
index f5ffdbe..b782416 100755 (executable)
@@ -35,6 +35,7 @@ import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
 import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.DataMapUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -220,6 +221,13 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         return new ResponseEntity<>(HttpStatus.CREATED);
     }
 
+    @Override
+    public ResponseEntity<Object> getModuleReferencesByCmHandle(final String cmHandle) {
+        final Collection<ModuleReference>
+            moduleReferences = networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle);
+        return new ResponseEntity<>(new Gson().toJson(moduleReferences), HttpStatus.OK);
+    }
+
     private DmiPluginRegistration convertRestObjectToJavaApiObject(
         final RestDmiPluginRegistration restDmiPluginRegistration) {
         return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class);
index 73ccd6e..613243e 100644 (file)
@@ -22,6 +22,8 @@
 
 package org.onap.cps.ncmp.rest.controller
 
+import org.onap.cps.spi.model.ModuleReference
+
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
@@ -246,5 +248,19 @@ class NetworkCmProxyControllerSpec extends Specification {
         and: 'resource is created'
             response.status == HttpStatus.CREATED.value()
     }
+
+    def 'Get module references for the given dataspace and cm handle.' () {
+        given: 'get module references url'
+            def getUrl = "$ncmpBasePathV1/ch/some-cmhandle/modules"
+        when: 'get module resource request is performed'
+            def response =mvc.perform(get(getUrl)).andReturn().response
+        then: 'ncmp service method to get yang resource module references is called'
+            mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle')
+                    >> [new ModuleReference(moduleName: 'some-name1',revision: 'some-revision1')]
+        and: 'response contains an array with the module name and revision'
+            response.getContentAsString() == '[{"moduleName":"some-name1","revision":"some-revision1"}]'
+        and: 'response returns an OK http code'
+            response.status == HttpStatus.OK.value()
+    }
 }
 
index fc01c3b..294b290 100644 (file)
@@ -26,7 +26,7 @@
 <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
 </parent>
 
index 0693f61..60669b9 100644 (file)
@@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.spi.model.ModuleReference;
 
 /*
  * Datastore interface for handling CPS data.
@@ -154,4 +155,12 @@ public interface NetworkCmProxyDataService {
                                                          @NotNull String resourceIdentifier,
                                                          @NotNull Object requestBody,
                                                          String contentType);
+
+    /**
+     * Retrieve module references for the given cm handle.
+     *
+     * @param cmHandle cm handle
+     * @return a collection of modules names and revisions
+     */
+    Collection<ModuleReference> getYangResourcesModuleReferences(@NotNull String cmHandle);
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/JsonUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/JsonUtils.java
new file mode 100644 (file)
index 0000000..6768777
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 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;
+
+public class JsonUtils {
+
+    private static final String BACK_SLASH = "\\";
+    private static final String NEW_LINE = "\n";
+    private static final String QUOTE = "\"";
+
+    private JsonUtils() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Remove redundant beginning and end characters.
+     * @param input string to format
+     * @return formatted string
+     */
+    public static String removeWrappingTokens(final String input) {
+        return input.substring(1, input.length() - 1);
+    }
+
+    /**
+     * Remove redundant escape characters.
+     * @param input string to format
+     * @return formatted string
+     */
+    public static String removeRedundantEscapeCharacters(final String input) {
+        return input.replace(BACK_SLASH + "n", NEW_LINE)
+            .replace(BACK_SLASH + QUOTE, QUOTE)
+            .replace(BACK_SLASH + BACK_SLASH + QUOTE, BACK_SLASH + QUOTE);
+    }
+}
index dfe5603..a28b73c 100755 (executable)
@@ -76,6 +76,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private static  final String NCMP_DMI_SERVICE_NAME = "dmi-service-name";
 
+    private  static final String REVISION = "revision";
+
     private CpsDataService cpsDataService;
 
     private ObjectMapper objectMapper;
@@ -88,8 +90,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private CpsAdminService cpsAdminService;
 
-    public static final String NO_NAMESPACE = null;
-
     /**
      * Constructor Injection for Dependencies.
      * @param dmiOperations DMI operation
@@ -215,7 +215,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
                 .cmHandleProperties(cmHandlePropertiesMap)
                 .build();
         final var dmiRequestBody = prepareOperationBody(dmiRequestBodyObject);
-        final ResponseEntity<Void> responseEntity = dmiOperations
+        final ResponseEntity<String> responseEntity = dmiOperations
                 .createResourceDataPassThroughRunningFromDmi(dmiServiceName,
                         cmHandle,
                         resourceIdentifier,
@@ -223,13 +223,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         handleResponseForPost(responseEntity);
     }
 
+    @Override
+    public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandle) {
+        return cpsModuleService.getYangResourcesModuleReferences(NF_PROXY_DATASPACE_NAME, cmHandle);
+    }
+
     private DataNode fetchDataNodeFromDmiRegistryForCmHandle(final String cmHandle) {
         final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']";
-        final var dataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
+        return cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
                 NCMP_DMI_REGISTRY_ANCHOR,
                 xpathForDmiRegistryToFetchCmHandle,
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
-        return dataNode;
     }
 
     private String prepareOperationBody(final GenericRequestBody requestBodyObject) {
@@ -242,9 +246,9 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
-    private Map<String, String> getCmHandlePropertiesAsMap(final Collection<DataNode> cmHandlePropertiesList) {
-        if (cmHandlePropertiesList == null || cmHandlePropertiesList.size() == 0) {
-            return null;
+    private static Map<String, String> getCmHandlePropertiesAsMap(final Collection<DataNode> cmHandlePropertiesList) {
+        if (cmHandlePropertiesList == null || cmHandlePropertiesList.isEmpty()) {
+            return Collections.emptyMap();
         }
         final Map<String, String> cmHandlePropertiesMap = new LinkedHashMap<>();
         for (final var node: cmHandlePropertiesList) {
@@ -254,7 +258,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return cmHandlePropertiesMap;
     }
 
-    private Object handleResponse(final @NotNull ResponseEntity<Object> responseEntity) {
+    private static Object handleResponse(final @NotNull ResponseEntity<Object> responseEntity) {
         if (responseEntity.getStatusCode() == HttpStatus.OK) {
             return responseEntity.getBody();
         } else {
@@ -264,8 +268,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
-    private void handleResponseForPost(final @NotNull ResponseEntity<Void> responseEntity) {
-        if (responseEntity.getStatusCode() != HttpStatus.CREATED) {
+    private static void handleResponseForPost(final @NotNull ResponseEntity<String> responseEntity) {
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
             throw new NcmpException("Not able to create resource data.",
                     "DMI status code: " + responseEntity.getStatusCodeValue()
                             + ", DMI response body: " + responseEntity.getBody());
@@ -275,11 +279,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private String getGenericRequestBody(final DataNode cmHandleDataNode) {
         final Collection<DataNode> cmHandlePropertiesList = cmHandleDataNode.getChildDataNodes();
         final Map<String, String> cmHandlePropertiesMap = getCmHandlePropertiesAsMap(cmHandlePropertiesList);
-        final var requetBodyObject = GenericRequestBody.builder()
+        final var requestBodyObject = GenericRequestBody.builder()
                 .operation(GenericRequestBody.OperationEnum.READ)
                 .cmHandleProperties(cmHandlePropertiesMap)
                 .build();
-        return prepareOperationBody(requetBodyObject);
+        return prepareOperationBody(requestBodyObject);
     }
 
     private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
@@ -295,10 +299,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
                 "/dmi-registry", cmHandlesJsonData, NO_TIMESTAMP);
         } catch (final JsonProcessingException e) {
-            log.error("Parsing error occurred while converting Object to JSON DMI Registry.");
-            throw new DataValidationException(
-                "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e
-                .getMessage(), e);
+            handleJsonProcessingException(dmiPluginRegistration, e);
         }
     }
 
@@ -310,26 +311,40 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
                 final PersistenceCmHandle persistenceCmHandle =
                     toPersistenceCmHandle(dmiPluginRegistration.getDmiPlugin(), cmHandle);
                 persistenceCmHandlesList.add(persistenceCmHandle);
-                createAnchorAndSyncModel(persistenceCmHandle);
             }
             final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
-            cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry",
-                cmHandleJsonData, NO_TIMESTAMP);
+
+            registerAndSyncNode(persistenceCmHandlesList, cmHandleJsonData);
         } catch (final JsonProcessingException e) {
-            log.error("Parsing error occurred while converting Object to JSON for DMI Registry.");
-            throw new DataValidationException(
-                "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e
-                .getMessage(), e);
+            handleJsonProcessingException(dmiPluginRegistration, e);
         }
     }
 
-    private PersistenceCmHandle toPersistenceCmHandle(final String dmiPluginService,
-                                                      final CmHandle cmHandle) {
+    private static void handleJsonProcessingException(final DmiPluginRegistration dmiPluginRegistration,
+                                                      final JsonProcessingException e) {
+        final String message = "Parsing error occurred while processing DMI Plugin Registration"
+            + dmiPluginRegistration;
+        log.error(message);
+        throw new DataValidationException(message, e.getMessage(), e);
+    }
+
+    private void registerAndSyncNode(final PersistenceCmHandlesList persistenceCmHandlesList,
+                                     final String cmHandleJsonData) {
+        cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry",
+            cmHandleJsonData, NO_TIMESTAMP);
+
+        for (final PersistenceCmHandle persistenceCmHandle : persistenceCmHandlesList.getPersistenceCmHandles()) {
+            createAnchorAndSyncModel(persistenceCmHandle);
+        }
+    }
+
+    private static PersistenceCmHandle toPersistenceCmHandle(final String dmiPluginService,
+                                                             final CmHandle cmHandle) {
         final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle();
         persistenceCmHandle.setDmiServiceName(dmiPluginService);
         persistenceCmHandle.setId(cmHandle.getCmHandleID());
         if (cmHandle.getCmHandleProperties() == null) {
-            persistenceCmHandle.setAdditionalProperties(Collections.EMPTY_MAP);
+            persistenceCmHandle.setAdditionalProperties(Collections.emptyMap());
         } else {
             persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties());
         }
@@ -350,31 +365,64 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     protected void createAnchorAndSyncModel(final PersistenceCmHandle cmHandle) {
         final var modulesForCmHandle =
             dmiOperations.getResourceFromDmi(cmHandle.getDmiServiceName(), cmHandle.getId(), "modules");
-
-        final List<ModuleReference> moduleReferencesFromDmiForCmHandle = getModuleReferences(modulesForCmHandle);
-
-        final var knownModuleReferencesInCps = cpsModuleService.getAllYangResourcesModuleReferences();
-
+        final List<ModuleReference> moduleReferencesFromDmiForCmHandle =
+            getModuleReferences(modulesForCmHandle);
+        final var knownModuleReferencesInCps =
+            cpsModuleService.getYangResourceModuleReferences(NF_PROXY_DATASPACE_NAME);
         final List<ModuleReference> existingModuleReferences = new ArrayList<>();
+
+        final List<ModuleReference> unknownModuleReferences = new ArrayList<>();
         for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromDmiForCmHandle) {
             if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) {
                 existingModuleReferences.add(moduleReferenceFromDmiForCmHandle);
+            } else {
+                unknownModuleReferences.add(moduleReferenceFromDmiForCmHandle);
             }
         }
 
+        final JsonObject requestBodyAsJson = getRequestBodyAsJson(unknownModuleReferences);
+
         final Map<String, String> newYangResourcesModuleNameToContentMap =
-            getNewYangResources(cmHandle);
+            getNewYangResources(cmHandle, requestBodyAsJson.toString());
 
-        cpsModuleService.createSchemaSetFromModules(NCMP_DATASPACE_NAME, cmHandle.getId(),
+        cpsModuleService.createSchemaSetFromModules(NF_PROXY_DATASPACE_NAME, cmHandle.getId(),
             newYangResourcesModuleNameToContentMap, existingModuleReferences);
 
-        cpsAdminService.createAnchor(NCMP_DATASPACE_NAME, cmHandle.getId(), cmHandle.getId());
+        cpsAdminService.createAnchor(NF_PROXY_DATASPACE_NAME, cmHandle.getId(), cmHandle.getId());
+    }
+
+    private static JsonObject getRequestBodyAsJson(final List<ModuleReference> unknownModuleReferences) {
+
+        final JsonObject requestBodyAsJson = new JsonObject();
+        requestBodyAsJson.addProperty("operation", "read");
+
+        final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(unknownModuleReferences);
+
+        final JsonObject data = new JsonObject();
+        data.add("modules", moduleReferencesAsJson);
+        requestBodyAsJson.add("data", data);
+
+        return requestBodyAsJson;
     }
 
-    private Map<String, String> getNewYangResources(final PersistenceCmHandle cmHandle) {
-        final var moduleResourcesAsJsonString =  dmiOperations.getResourceFromDmi(
-            cmHandle.getDmiServiceName(), cmHandle.getId(), "moduleResources");
-        final JsonArray moduleResources = new Gson().fromJson(moduleResourcesAsJsonString.getBody(), JsonArray.class);
+    private static JsonArray getModuleReferencesAsJson(final List<ModuleReference> unknownModuleReferences) {
+        final JsonArray moduleReferences = new JsonArray();
+
+        for (final ModuleReference moduleReference : unknownModuleReferences) {
+            final JsonObject moduleReferenceAsJson = new JsonObject();
+            moduleReferenceAsJson.addProperty("name", moduleReference.getModuleName());
+            moduleReferenceAsJson.addProperty(REVISION, moduleReference.getRevision());
+            moduleReferences.add(moduleReferenceAsJson);
+        }
+        return moduleReferences;
+    }
+
+    private Map<String, String> getNewYangResources(final PersistenceCmHandle cmHandle, final String jsonData) {
+        final var moduleResourcesAsJsonString =  dmiOperations.getResourceFromDmiWithJsonData(
+            cmHandle.getDmiServiceName(), jsonData, cmHandle.getId(), "moduleResources");
+
+        final JsonArray moduleResources = new Gson().fromJson(moduleResourcesAsJsonString.getBody(),
+            JsonArray.class);
         final Map<String, String> newYangResourcesModuleNameToContentMap = new HashMap<>();
 
         for (final JsonElement moduleResource : moduleResources) {
@@ -384,29 +432,34 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return newYangResourcesModuleNameToContentMap;
     }
 
-    private YangResource toYangResource(final JsonObject yangResourceAsJson) {
+    private static YangResource toYangResource(final JsonObject yangResourceAsJson) {
         final YangResource yangResource = new YangResource();
         yangResource.setModuleName(yangResourceAsJson.get("moduleName").getAsString());
         yangResource.setRevision(yangResourceAsJson.get("revision").getAsString());
-        yangResource.setYangSource(yangResourceAsJson.get("yangSource").getAsString());
+        final String yangSourceJson = yangResourceAsJson.get("yangSource").getAsString();
+
+        String yangSource = JsonUtils.removeWrappingTokens(yangSourceJson);
+        yangSource = JsonUtils.removeRedundantEscapeCharacters(yangSource);
+        yangResource.setYangSource(yangSource);
+
         return yangResource;
     }
 
-    private List<ModuleReference> getModuleReferences(final ResponseEntity<String> response) {
+    private static List<ModuleReference> getModuleReferences(final ResponseEntity<String> response) {
         final List<ModuleReference> modulesFromDmiForCmHandle = new ArrayList<>();
         final JsonObject convertedObject = new Gson().fromJson(response.getBody(), JsonObject.class);
         final JsonArray moduleReferencesAsJson = convertedObject.getAsJsonArray("schemas");
         for (final JsonElement moduleReferenceAsJson : moduleReferencesAsJson) {
-            final ModuleReference moduleReference = toModuleReference((JsonObject) moduleReferenceAsJson);
+            final ModuleReference moduleReference =
+                toModuleReference((JsonObject) moduleReferenceAsJson);
             modulesFromDmiForCmHandle.add(moduleReference);
         }
         return modulesFromDmiForCmHandle;
     }
 
-    private ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) {
+    private static ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) {
         final var moduleReference = new ModuleReference();
-        moduleReference.setName(moduleReferenceAsJson.get("moduleName").getAsString());
-        moduleReference.setNamespace(NO_NAMESPACE);
+        moduleReference.setModuleName(moduleReferenceAsJson.get("moduleName").getAsString());
         moduleReference.setRevision(moduleReferenceAsJson.get("revision").getAsString());
         return moduleReference;
     }
index af691f6..fc70708 100644 (file)
@@ -46,10 +46,18 @@ public class DmiRestClient {
         return restTemplate.exchange(dmiResourceUrl, HttpMethod.PUT, httpEntity, Object.class);
     }
 
-    public ResponseEntity<Void> postOperationWithJsonData(final String dmiResourceUrl,
-                                                            final String jsonData, final HttpHeaders headers) {
-        final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(headers));
-        return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Void.class);
+    /**
+     * Sends POST operation to DMI with json body containing module references.
+     * @param dmiResourceUrl dmi resource url
+     * @param jsonData json data body
+     * @param httpHeaders http headers
+     * @return response entity of type String
+     */
+    public ResponseEntity<String> postOperationWithJsonData(final String dmiResourceUrl,
+                                                            final String jsonData,
+                                                            final HttpHeaders httpHeaders) {
+        final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(httpHeaders));
+        return restTemplate.postForEntity(dmiResourceUrl, httpEntity, String.class);
     }
 
     private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders) {
@@ -58,6 +66,12 @@ public class DmiRestClient {
         return httpHeaders;
     }
 
+    /**
+     * Sends POST operation to DMI.
+     * @param dmiResourceUrl dmi resource url
+     * @param httpHeaders http headers
+     * @return response entity of type String
+     */
     public ResponseEntity<String> postOperation(final String dmiResourceUrl, final HttpHeaders httpHeaders) {
         final var httpEntity = new HttpEntity<>(configureHttpHeaders(httpHeaders));
         return restTemplate.exchange(dmiResourceUrl, HttpMethod.POST, httpEntity, String.class);
index a834bfc..c4e82d3 100644 (file)
@@ -41,7 +41,7 @@ public class NcmpConfiguration {
     }
 
     @Bean
-    public RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) {
+    public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) {
         return restTemplateBuilder.build();
     }
-}
\ No newline at end of file
+}
index ff53464..2c75b5d 100644 (file)
@@ -43,17 +43,5 @@ public class NcmpException extends RuntimeException {
         this.details = details;
     }
 
-    /**
-     * Constructor.
-     *
-     * @param message the error message
-     * @param details the error details
-     * @param cause   the cause of the exception
-     */
-    public NcmpException(final String message, final String details, final Throwable cause) {
-        super(message, cause);
-        this.details = details;
-    }
-
 }
 
index d6feaf3..71af3d4 100644 (file)
@@ -76,7 +76,23 @@ public class DmiOperations {
         final var dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
         final var httpHeaders = new HttpHeaders();
         return dmiRestClient.postOperation(dmiResourceDataUrl, httpHeaders);
+    }
 
+    /**
+     * Get resources from DMI for modules.
+     *
+     * @param dmiServiceName dmi service name
+     * @param jsonData module names and revisions as JSON
+     * @param cmHandle cmHandle
+     * @param resourceName name of the resource(s)
+     * @return {@code ResponseEntity} response entity
+     */
+    public ResponseEntity<String> getResourceFromDmiWithJsonData(final String dmiServiceName,
+                                                               final String jsonData,
+                                                               final String cmHandle,
+                                                               final String resourceName) {
+        final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
+        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, new HttpHeaders());
     }
 
     /**
@@ -141,7 +157,7 @@ public class DmiOperations {
      * @param jsonBody    json body for put operation
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<Void> createResourceDataPassThroughRunningFromDmi(final String dmiServiceName,
+    public ResponseEntity<String> createResourceDataPassThroughRunningFromDmi(final String dmiServiceName,
                                                                             final String cmHandle,
                                                                             final String resourceId,
                                                                             final String jsonBody) {
index beeb00f..f35abf6 100644 (file)
@@ -23,11 +23,13 @@ package org.onap.cps.ncmp.api.models;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.ArrayList;
 import java.util.List;
+import lombok.Getter;
 
+@Getter
 public class PersistenceCmHandlesList {
 
     @JsonProperty("cm-handles")
-    private List<PersistenceCmHandle> persistenceCmHandles;
+    private List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>();
 
     /**
      * Add a persistenceCmHandle.
@@ -35,10 +37,6 @@ public class PersistenceCmHandlesList {
      * @param persistenceCmHandle the persistenceCmHandle to add
      */
     public void add(final PersistenceCmHandle persistenceCmHandle) {
-        if (persistenceCmHandles == null) {
-            persistenceCmHandles = new ArrayList<>();
-        }
         persistenceCmHandles.add(persistenceCmHandle);
     }
-
 }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/JsonUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/JsonUtilsSpec.groovy
new file mode 100644 (file)
index 0000000..be27dfa
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 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
+
+import spock.lang.Specification
+
+class JsonUtilsSpec extends Specification  {
+    def 'Remove redundant escape characters.'() {
+        expect: 'removing redundant escape characters returns the correct output for #scenario'
+            JsonUtils.removeRedundantEscapeCharacters(input) == expectedOutput
+        where: 'the following input is used'
+            scenario                               | input                                      || expectedOutput
+            'two lines'                            | 'line1\\nline2'                            || 'line1\nline2'
+            'a string inside quotes'               | 'a \\"word in quotes\\"'                   || 'a "word in quotes"'
+            'quotes inside quotes (double escape)' | '\\"quotes \\\\\\"inside\\\\\\" quotes\\"' || '"quotes \\"inside\\" quotes"'  // human readable:  "quotes \"inside\" quotes"
+    }
+    def 'Remove wrapping tokens.'() {
+        expect: 'removing wrapping tokens returns the correct output for #scenario'
+            JsonUtils.removeWrappingTokens(input) == expectedOutput
+        where: 'the following input is used'
+            scenario                           | input    || expectedOutput
+            'a string in quotes'               | '"abc"'  || 'abc'
+            'a string in apostrophes'          | "'abc'"  || 'abc'
+            'a string inside any other tokens' | 'abcde'  || 'bcd'
+    }
+
+    def 'Cannot use constructor.'() {
+        when: 'attempt to construct object'
+            new JsonUtils()
+        then: 'an exception is thrown'
+            thrown(IllegalStateException)
+    }
+}
+
index b42db57..8739355 100644 (file)
@@ -36,6 +36,8 @@ import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.models.PersistenceCmHandle
 import org.onap.cps.ncmp.utils.TestUtils
 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.ModuleReference
 import org.springframework.http.HttpStatus
@@ -56,16 +58,15 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockCpsAdminService = Mock(CpsAdminService)
     def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties)
+    def spyObjectMapper = Spy(ObjectMapper)
 
     def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
-            mockCpsDataService, mockCpsQueryService, mockCpsAdminService, new ObjectMapper())
+            mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper)
 
     def cmHandle = 'some handle'
     def noTimestamp = null
     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
     def cmHandleForModelSync = new PersistenceCmHandle(id:'some cm handle', dmiServiceName: 'some service name')
-    def expectedDataspaceNameForModleSync = 'NCMP-Admin'
-    def NO_NAMESPACE = null
 
     def expectedDataspaceName = 'NFP-Operational'
     def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
@@ -156,12 +157,12 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
 
         where:
-            scenario                        | createdCmHandles       | updatedCmHandles       | removedCmHandles || expectedCallsToSaveNode   | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
-            'create'                        | [persistenceCmHandle ] | []                     | []               || 1                         | 0                         | 0
-            'update'                        | []                     | [persistenceCmHandle ] | []               || 0                         | 1                         | 0
-            'delete'                        | []                     | []                     | cmHandlesArray   || 0                         | 0                         | 1
-            'create, update and delete'     | [persistenceCmHandle ] | [persistenceCmHandle ] | cmHandlesArray   || 1                         | 1                         | 1
-
+            scenario                        | createdCmHandles      | updatedCmHandles      | removedCmHandles || expectedCallsToSaveNode   | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
+            'create'                        | [persistenceCmHandle] | []                    | []               || 1                         | 0                         | 0
+            'update'                        | []                    | [persistenceCmHandle] | []               || 0                         | 1                         | 0
+            'delete'                        | []                    | []                    | cmHandlesArray   || 0                         | 0                         | 1
+            'create, update and delete'     | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray   || 1                         | 1                         | 1
+            'no valid data'                 | null                  | null                  |  null            || 0                         | 0                         | 0
     }
 
     def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
@@ -171,7 +172,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             dmiPluginRegistration.dmiPlugin = 'my-server'
             persistenceCmHandle.cmHandleID = '123'
             persistenceCmHandle.cmHandleProperties = null
-            dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
+            dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
         when: 'registration is updated'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
@@ -180,28 +181,58 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 '/dmi-registry', expectedJsonData, noTimestamp)
     }
 
+    def 'Register a DMI Plugin with JSON processing errors during #scenario.'() {
+        given: 'a registration without cmHandle properties '
+            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
+            def dmiPluginRegistration = new DmiPluginRegistration()
+            dmiPluginRegistration.createdCmHandles = createdCmHandles
+            dmiPluginRegistration.updatedCmHandles = updatedCmHandles
+        and: 'an JSON processing exception occurs'
+            spyObjectMapper.writeValueAsString(_) >> { throw (new JsonProcessingException('')) }
+        when: 'registration is updated'
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a data validation exception is thrown'
+            thrown(DataValidationException)
+        where:
+            scenario | createdCmHandles      | updatedCmHandles
+            'create' | [persistenceCmHandle] | []
+            'update' | []                    | [persistenceCmHandle]
+    }
+
+    def 'Register a DMI Plugin with no data found during delete.'() {
+        given: 'a registration without cmHandle properties '
+            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
+            def dmiPluginRegistration = new DmiPluginRegistration()
+            dmiPluginRegistration.removedCmHandles = ['some cm handle']
+        and: 'an JSON processing exception occurs'
+            mockCpsDataService.deleteListNodeData(*_) >>  { throw (new DataNodeNotFoundException('','')) }
+        when: 'registration is updated'
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'no exception is thrown'
+            noExceptionThrown()
+    }
+
     def 'Get resource data for pass-through operational from dmi.'() {
         given: 'data node representing cmHandle and its properties'
             def cmHandleDataNode = getCmHandleDataNodeForTest()
+        and: 'data node is got from data service'
+            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
+                cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+        and: 'resource data is got from DMI'
+            mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
+                'testCmHandle',
+                'testResourceId',
+                'testFieldQuery',
+                5,
+                'testAcceptParam',
+                '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
         when: 'get resource data is called'
             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
             'testResourceId',
             'testAcceptParam',
             'testFieldQuery',
             5)
-        then: 'cps data service is being called once to get data node'
-            1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
-                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
-        and: 'dmi operation is being called to get resource data'
-            1 * mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
-                    'testCmHandle',
-                    'testResourceId',
-                    'testFieldQuery',
-                    5,
-                    'testAcceptParam',
-            '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >>
-                new ResponseEntity<>('result-json', HttpStatus.OK)
-        and: 'dmi returns ok response'
+        then: 'dmi returns ok response'
             response == 'result-json'
     }
 
@@ -214,15 +245,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         and: 'objectMapper not able to parse object'
             def mockObjectMapper = Mock(ObjectMapper)
             objectUnderTest.objectMapper = mockObjectMapper
-            mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
+            mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') }
         when: 'get resource data is called'
             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
                     'testResourceId',
                     'testAcceptParam',
                     'testFieldQuery',
                     5)
-        then: 'exception is thrown'
-            thrown(NcmpException.class)
+        then: 'exception is thrown with the expected details'
+            def exceptionThrown = thrown(NcmpException.class)
+            exceptionThrown.details == 'testException'
     }
 
     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
@@ -247,7 +279,9 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                     'testFieldQuery',
                     5)
         then: 'exception is thrown'
-            thrown(NcmpException.class)
+            def exceptionThrown = thrown(NcmpException.class)
+        and: 'details contains the original response'
+            exceptionThrown.details.contains('NOK-json')
     }
 
     def 'Get resource data for pass-through running from dmi.'() {
@@ -283,15 +317,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         and: 'objectMapper not able to parse object'
             def mockObjectMapper = Mock(ObjectMapper)
             objectUnderTest.objectMapper = mockObjectMapper
-            mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
+            mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') }
         when: 'get resource data is called'
             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
                     'testResourceId',
                     'testAcceptParam',
                     'testFieldQuery',
                     5)
-        then: 'exception is thrown'
-            thrown(NcmpException.class)
+        then: 'exception is thrown with the expected details'
+            def exceptionThrown = thrown(NcmpException.class)
+            exceptionThrown.details == 'testException'
     }
 
     def 'Get resource data for pass-through running from dmi return NOK response.'() {
@@ -316,13 +351,15 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                     'testFieldQuery',
                     5)
         then: 'exception is thrown'
-            thrown(NcmpException.class)
+            def exceptionThrown = thrown(NcmpException.class)
+        and: 'details contains the original response'
+            exceptionThrown.details.contains('NOK-json')
     }
 
     def 'Write resource data for pass-through running from dmi using POST.'() {
         given: 'data node representing cmHandle and its properties'
             def cmHandleDataNode = getCmHandleDataNodeForTest()
-        and: 'cpsDataService returns valid dataNode'
+        and: 'cpsDataService returns valid cm-handle datanode'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
         when: 'get resource data is called'
@@ -334,7 +371,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testCmHandle',
                 'testResourceId',
                 '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":{"testName":"testValue"}}')
-                >> { new ResponseEntity<>(HttpStatus.CREATED) }
+                >> { new ResponseEntity<>(HttpStatus.OK) }
     }
 
     def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
@@ -344,36 +381,45 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
         and: 'dmi throws exception'
-            1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String)
+            mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String)
                     >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
         when: 'get resource data is called'
             objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
                     'testResourceId',
                     '{some-json}', 'application/json')
         then: 'exception is thrown'
-            thrown(NcmpException.class)
+            def exceptionThrown = thrown(NcmpException.class)
+        and: 'details contains (not found) error code: 404'
+            exceptionThrown.details.contains('404')
     }
 
     def 'Sync model for a (new) cm handle with #scenario'() {
         given: 'DMI PLug-in returns a list of module references'
             getModulesForCmHandle()
-            def knownModule1 = new ModuleReference('module1', NO_NAMESPACE, '1')
-            def knownOtherModule = new ModuleReference('some other module', NO_NAMESPACE, 'some revision')
+            def knownModule1 = new ModuleReference('module1', '1')
+            def knownOtherModule = new ModuleReference('some other module', 'some revision')
         and: 'CPS-Core returns list of known modules'
-            mockCpsModuleService.getAllYangResourcesModuleReferences() >> [knownModule1, knownOtherModule]
+            mockCpsModuleService.getYangResourceModuleReferences(_) >> [knownModule1, knownOtherModule]
         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
             def moduleResources = new ResponseEntity<String>(sdncReponseBody, HttpStatus.OK)
-            mockDmiOperations.getResourceFromDmi(_, cmHandleForModelSync.getId(), 'moduleResources') >> moduleResources
+            mockDmiOperations.getResourceFromDmiWithJsonData(_, _, _, 'moduleResources') >> moduleResources
         when: 'module Sync is triggered'
             objectUnderTest.createAnchorAndSyncModel(cmHandleForModelSync)
         then: 'the CPS module service is called once with the correct parameters'
-            1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceNameForModleSync, cmHandleForModelSync.getId(), expectedYangResourceToContentMap , [knownModule1])
+            1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), expectedYangResourceToContentMap, [knownModule1])
         and: 'admin service create anchor method has been called with correct parameters'
-            1 * mockCpsAdminService.createAnchor(expectedDataspaceNameForModleSync, cmHandleForModelSync.getId(), cmHandleForModelSync.getId())
+            1 * mockCpsAdminService.createAnchor(expectedDataspaceName, cmHandleForModelSync.getId(), cmHandleForModelSync.getId())
         where: 'the following responses are recieved from SDNC'
-            scenario             | sdncReponseBody                                                             || expectedYangResourceToContentMap
-            'one unknown module' | '[{"moduleName" : "someModule", "revision" : "1","yangSource": "someResource"}]' || [someModule: 'someResource']
-            'no unknown module'  | '[]'                                                                        || [:]
+            scenario             | sdncReponseBody                                                                        || expectedYangResourceToContentMap
+            'one unknown module' | '[{"moduleName" : "someModule", "revision" : "1","yangSource": "[some yang source]"}]' || [someModule: 'some yang source']
+            'no unknown module'  | '[]'                                                                                   || [:]
+    }
+
+    def 'Getting Yang Resources.'() {
+        when: 'yang resources is called'
+            objectUnderTest.getYangResourcesModuleReferences('some cm handle')
+        then: 'CPS module services is invoked for the correct dataspace and cm handle'
+            1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some cm handle')
     }
 
     def getModulesForCmHandle() {
@@ -387,7 +433,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def getObjectUnderTestWithModelSyncDisabled() {
         def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
-                mockCpsDataService, mockCpsQueryService, mockCpsAdminService, new ObjectMapper()))
+                mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper))
         objectUnderTest.createAnchorAndSyncModel(_) >> null
         return objectUnderTest
     }
index 879c73c..bf6179b 100644 (file)
 package org.onap.cps.ncmp.api.impl.client
 
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
+import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.http.HttpEntity
 import org.springframework.http.HttpHeaders
+import org.springframework.http.HttpMethod
 import org.springframework.http.ResponseEntity
+import org.springframework.test.context.ContextConfiguration
 import org.springframework.web.client.RestTemplate
 import spock.lang.Specification
-import  org.springframework.http.HttpMethod
 
+@SpringBootTest
+@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient])
 class DmiRestClientSpec extends Specification {
 
-    def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties)
-    def mockRestTemplate = Mock(RestTemplate)
-    def objectUnderTest = new DmiRestClient(mockRestTemplate, mockDmiProperties)
+    @SpringBean
+    RestTemplate mockRestTemplate = Mock(RestTemplate)
+
+    @Autowired
+    DmiRestClient objectUnderTest
 
     def 'DMI PUT operation.'() {
         given: 'a PUT url'
             def getResourceDataUrl = 'http://some-uri/getResourceDataUrl'
-        and: 'dmi properties'
-            setupTestConfigurationData()
         and: 'the rest template returns a valid response entity'
             def mockResponseEntity = Mock(ResponseEntity)
             mockRestTemplate.exchange(getResourceDataUrl, HttpMethod.PUT, _ as HttpEntity, Object.class) >> mockResponseEntity
@@ -51,19 +57,13 @@ class DmiRestClientSpec extends Specification {
     def 'DMI POST operation.'() {
         given: 'a POST url'
             def getResourceDataUrl = 'http://some-uri/createResourceDataUrl'
-        and: 'dmi properties'
-            setupTestConfigurationData()
         and: 'the rest template returns a valid response entity'
             def mockResponseEntity = Mock(ResponseEntity)
-            mockRestTemplate.postForEntity(getResourceDataUrl, _ as HttpEntity, Void.class) >> mockResponseEntity
+            mockRestTemplate.postForEntity(getResourceDataUrl, _ as HttpEntity, String.class) >> mockResponseEntity
         when: 'POST operation is invoked'
             def result = objectUnderTest.postOperationWithJsonData(getResourceDataUrl, 'json-data', new HttpHeaders())
         then: 'the output of the method is equal to the output from the test template'
             result == mockResponseEntity
     }
 
-    def setupTestConfigurationData() {
-        mockDmiProperties.authUsername >> 'some-username'
-        mockDmiProperties.authPassword >> 'some-password'
-    }
-}
\ No newline at end of file
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy
new file mode 100644 (file)
index 0000000..dd4c137
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 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.config
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.boot.web.client.RestTemplateBuilder
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.web.client.RestTemplate
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties])
+class NcmpConfigurationSpec extends Specification{
+
+    @Autowired
+    NcmpConfiguration.DmiProperties dmiProperties
+
+    def 'DMI Properties.'() {
+        expect: 'properties are set to values in test configuration yaml file'
+            dmiProperties.authUsername == 'some-user'
+            dmiProperties.authPassword == 'some-password'
+    }
+
+    def 'Rest Template creation.'() {
+        given: 'a rest template builder'
+            def mockRestTemplateBuilder = Mock(RestTemplateBuilder)
+            def expectedRestTemplate = Mock(RestTemplate)
+            mockRestTemplateBuilder.build() >> expectedRestTemplate
+        when: 'a rest template is created'
+            def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder)
+        then: 'the rest template from the builder is returned'
+            assert result == expectedRestTemplate
+    }
+}
index 987ab2b..6a1ce1a 100644 (file)
@@ -92,4 +92,18 @@ class DmiOperationsSpec extends Specification {
         then: 'the post operation is executed with the correct URL'
             1 * mockDmiRestClient.postOperation(expectedUrl, _ as HttpHeaders)
     }
+
+    def 'Call get resource from dmi with json data.'() {
+        given: 'expected url & json data'
+            def requestBody = 'some json'
+            def expectedUrl = 'testDmiBasePath/dmi/v1/ch/testCmHandle/modules'
+            def expectedHttpHeaders = new HttpHeaders()
+        when: 'get resource data is called to dmi'
+            objectUnderTest.getResourceFromDmiWithJsonData('testDmiBasePath',
+                    requestBody,
+                    'testCmHandle',
+                    'modules')
+        then: 'the post operation is executed with the correct URL and json data'
+            1 * mockDmiRestClient.postOperationWithJsonData(expectedUrl, requestBody, expectedHttpHeaders)
+    }
 }
\ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/PersistenceCmHandleSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/PersistenceCmHandleSpec.groovy
new file mode 100644 (file)
index 0000000..bfed795
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+package org.onap.cps.ncmp.api.models
+
+import spock.lang.Specification
+
+class PersistenceCmHandleSpec extends Specification {
+
+    def objectUnderTest = new PersistenceCmHandle()
+
+    def 'Setting and getting additional properties.'() {
+        given: 'a map of one property is added'
+            objectUnderTest.setAdditionalProperties([myProperty: 'some value'])
+        when: 'the additional properties are retrieved'
+            def result = objectUnderTest.getAdditionalProperties()
+        then: 'the result has the right size'
+            assert result.size() == 1
+        and: 'the property in the result has the correct name and value'
+            def actualAdditionalProperty = result.get(0)
+            def expectedAdditionalProperty = new PersistenceCmHandle.AdditionalProperty('myProperty','some value')
+            assert actualAdditionalProperty.name == expectedAdditionalProperty.name
+            assert actualAdditionalProperty.value == expectedAdditionalProperty.value
+    }
+
+}
index d6cb463..444a258 100644 (file)
@@ -1,14 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
 package org.onap.cps.ncmp.api.models
 
-import org.onap.cps.spi.model.ModuleReference
+import org.onap.cps.spi.model.ExtendedModuleReference
 import spock.lang.Specification
 
 class moduleReferenceSpec extends Specification {
 
     def 'lombok data annotation correctly implements toString() and hashCode() methods'() {
         given: 'two moduleReference objects'
-            def moduleReference1 = new ModuleReference('module1', "some namespace", '1')
-            def moduleReference2 = new ModuleReference('module1', "some namespace", '1')
+            def moduleReference1 = new ExtendedModuleReference('module1', "some namespace", '1')
+            def moduleReference2 = new ExtendedModuleReference('module1', "some namespace", '1')
         when: 'lombok generated methods are called'
         then: 'the methods exist and behaviour is accurate'
             assert moduleReference1.toString() == moduleReference2.toString()
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
new file mode 100644 (file)
index 0000000..71ac2c9
--- /dev/null
@@ -0,0 +1,23 @@
+#  ============LICENSE_START=======================================================
+#  Copyright (C) 2021 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=========================================================
+
+dmi:
+    auth:
+        username: some-user
+        password: some-password
+
index e886100..3de50c3 100755 (executable)
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
index 38ba9ac..cb454c2 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>1.1.0-SNAPSHOT</version>
+        <version>2.0.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index c4af380..a3e5fd9 100755 (executable)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>1.1.0-SNAPSHOT</version>
+        <version>2.0.1-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 273cb2e..00911f8 100644 (file)
@@ -26,7 +26,7 @@
     <parent>\r
         <groupId>org.onap.cps</groupId>\r
         <artifactId>cps-parent</artifactId>\r
-        <version>1.1.0-SNAPSHOT</version>\r
+        <version>2.0.1-SNAPSHOT</version>\r
         <relativePath>../cps-parent/pom.xml</relativePath>\r
     </parent>\r
 \r
             <groupId>org.apache.commons</groupId>\r
             <artifactId>commons-lang3</artifactId>\r
         </dependency>\r
+        <dependency>\r
+            <groupId>com.fasterxml.jackson.core</groupId>\r
+            <artifactId>jackson-databind</artifactId>\r
+        </dependency>\r
         <!-- T E S T   D E P E N D E N C I E S -->\r
         <dependency>\r
             <groupId>org.codehaus.groovy</groupId>\r
         </dependency>\r
     </dependencies>\r
 \r
+\r
     <build>\r
         <plugins>\r
             <plugin>\r
index af010f4..c57723d 100644 (file)
@@ -24,12 +24,15 @@ package org.onap.cps.spi.impl;
 
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -50,6 +53,7 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.ConcurrencyException;
 import org.onap.cps.spi.exceptions.CpsPathException;
 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 org.onap.cps.spi.repository.AnchorRepository;
@@ -68,6 +72,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     private FragmentRepository fragmentRepository;
 
+    private final ObjectMapper objectMapper;
+
     /**
      * Constructor.
      *
@@ -80,11 +86,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         this.dataspaceRepository = dataspaceRepository;
         this.anchorRepository = anchorRepository;
         this.fragmentRepository = fragmentRepository;
+        this.objectMapper = new ObjectMapper();
     }
 
     private static final Gson GSON = new GsonBuilder().create();
     private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
-    private static final String REG_EX_FOR_LIST_NODE_KEY = "\\[(\\@([^/]*?))+( and)*\\]$";
+    private static final String REG_EX_FOR_LIST_NODE_KEY = "\\[(\\@([^/]*?)){0,99}( and)*\\]$";
 
     @Override
     public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath,
@@ -236,17 +243,27 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return ancestorXpath;
     }
 
-    private static DataNode toDataNode(final FragmentEntity fragmentEntity,
+    private DataNode toDataNode(final FragmentEntity fragmentEntity,
         final FetchDescendantsOption fetchDescendantsOption) {
-        final Map<String, Object> leaves = GSON.fromJson(fragmentEntity.getAttributes(), Map.class);
         final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption);
+        Map<String, Object> leaves = new HashMap<>();
+        if (fragmentEntity.getAttributes() != null) {
+            try {
+                leaves = objectMapper.readValue(fragmentEntity.getAttributes(), Map.class);
+            } catch (final JsonProcessingException jsonProcessingException) {
+                final String message = "Parsing error occurred while processing fragmentEntity attributes.";
+                log.error(message);
+                throw new DataValidationException(message,
+                    jsonProcessingException.getMessage(), jsonProcessingException);
+            }
+        }
         return new DataNodeBuilder()
             .withXpath(fragmentEntity.getXpath())
             .withLeaves(leaves)
             .withChildDataNodes(childDataNodes).build();
     }
 
-    private static List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
+    private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
         final FetchDescendantsOption fetchDescendantsOption) {
         if (fetchDescendantsOption == INCLUDE_ALL_DESCENDANTS) {
             return fragmentEntity.getChildFragments().stream()
@@ -327,10 +344,10 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(descendantNode);
 
         final boolean xpathPointsToAValidChildNodeWithKey = parentEntity.getChildFragments().stream().anyMatch(
-            (fragment) -> fragment.getXpath().equals(listNodeXpath));
+            fragment -> fragment.getXpath().equals(listNodeXpath));
 
         final boolean xpathPointsToAValidChildNodeWithoutKey = parentEntity.getChildFragments().stream().anyMatch(
-            (fragment) -> fragment.getXpath().replaceAll(REG_EX_FOR_LIST_NODE_KEY, "").equals(listNodeXpath));
+            fragment -> fragment.getXpath().replaceAll(REG_EX_FOR_LIST_NODE_KEY, "").equals(listNodeXpath));
 
         if ((descendantNodeHasListNodeKey.find() && xpathPointsToAValidChildNodeWithKey)
             ||
index 1c7828f..e0f5426 100755 (executable)
@@ -116,11 +116,21 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
     }
 
     @Override
-    public List<ModuleReference> getAllYangResourcesModuleReferences() {
-        final List<YangResourceModuleReference> yangResourceModuleReferenceList =
-                yangResourceRepository.findAllModuleNameAndRevision();
+    public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) {
+        final Set<YangResourceModuleReference> yangResourceModuleReferenceList =
+            yangResourceRepository.findAllModuleReferences(dataspaceName);
         return yangResourceModuleReferenceList.stream().map(CpsModulePersistenceServiceImpl::toModuleReference)
-                .collect(Collectors.toList());
+            .collect(Collectors.toList());
+    }
+
+    @Override
+    public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName,
+        final String anchorName) {
+        final Set<YangResourceModuleReference> yangResourceModuleReferenceList =
+            yangResourceRepository
+                .findAllModuleReferences(dataspaceName, anchorName);
+        return yangResourceModuleReferenceList.stream().map(CpsModulePersistenceServiceImpl::toModuleReference)
+            .collect(Collectors.toList());
     }
 
     @Override
@@ -148,15 +158,15 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
     @Transactional
     public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
                                           final Map<String, String> newYangResourcesModuleNameToContentMap,
-                                          final List<ModuleReference> moduleReferenceList) {
+                                          final List<ModuleReference> moduleReferences) {
         storeSchemaSet(dataspaceName, schemaSetName, newYangResourcesModuleNameToContentMap);
         final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final var schemaSetEntity =
                 schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
         final List<Long> listOfYangResourceIds = new ArrayList<>();
-        moduleReferenceList.forEach(moduleReference ->
+        moduleReferences.forEach(moduleReference ->
                 listOfYangResourceIds.add(yangResourceRepository.getIdByModuleNameAndRevision(
-                        moduleReference.getName(), moduleReference.getRevision())));
+                        moduleReference.getModuleName(), moduleReference.getRevision())));
         yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntity.getId(), listOfYangResourceIds);
     }
 
@@ -325,10 +335,11 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
         return checksum;
     }
 
-    private static ModuleReference toModuleReference(final YangResourceModuleReference yangResourceModuleReference) {
+    private static ModuleReference toModuleReference(
+        final YangResourceModuleReference yangResourceModuleReference) {
         return ModuleReference.builder()
-                .name(yangResourceModuleReference.getModuleName())
-                .revision(yangResourceModuleReference.getRevision())
-                .build();
+            .moduleName(yangResourceModuleReference.getModuleName())
+            .revision(yangResourceModuleReference.getRevision())
+            .build();
     }
 }
index 0b48eaa..b16b284 100644 (file)
@@ -37,8 +37,32 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
 
     List<YangResourceEntity> findAllByChecksumIn(@NotNull Set<String> checksum);
 
-    @Query(value = "SELECT module_name, revision FROM yang_resource", nativeQuery = true)
-    List<YangResourceModuleReference> findAllModuleNameAndRevision();
+    @Query(value = "SELECT DISTINCT\n"
+        + "yr.module_name AS module_name,\n"
+        + "yr.revision AS revision\n"
+        + "FROM\n"
+        + "dataspace d\n"
+        + "JOIN schema_set ss ON ss.dataspace_id = d.id\n"
+        + "JOIN schema_set_yang_resources ssyr ON ssyr.schema_set_id = ss.id\n"
+        + "JOIN yang_resource yr ON yr.id = ssyr.yang_resource_id\n"
+        + "WHERE\n"
+        + "d.name = :dataspaceName", nativeQuery = true)
+    Set<YangResourceModuleReference> findAllModuleReferences(@Param("dataspaceName") String dataspaceName);
+
+    @Query(value = "SELECT DISTINCT\n"
+        + "yr.module_Name AS module_name,\n"
+        + "yr.revision AS revision\n"
+        + "FROM\n"
+        + "dataspace d\n"
+        + "JOIN anchor a ON a.dataspace_id = d.id\n"
+        + "JOIN schema_set ss ON ss.dataspace_id = a.dataspace_id\n"
+        + "JOIN schema_set_yang_resources ssyr ON ssyr.schema_set_id = ss.id\n"
+        + "JOIN yang_resource yr ON yr.id = ssyr.yang_resource_id\n"
+        + "WHERE\n"
+        + "d.name = :dataspaceName AND\n"
+        + "a.name =:anchorName", nativeQuery = true)
+    Set<YangResourceModuleReference> findAllModuleReferences(
+        @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName);
 
     @Query(value = "SELECT id FROM yang_resource WHERE module_name=:name and revision=:revision", nativeQuery = true)
     Long getIdByModuleNameAndRevision(@Param("name") String moduleName, @Param("revision") String revision);
index 8217a4f..e2316e8 100755 (executable)
  */
 package org.onap.cps.spi.impl
 
-import org.onap.cps.spi.exceptions.DataValidationException
-
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-
 import com.google.common.collect.ImmutableSet
 import com.google.gson.Gson
 import com.google.gson.GsonBuilder
@@ -42,6 +37,9 @@ import org.springframework.test.context.jdbc.Sql
 
 import javax.validation.ConstraintViolationException
 
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+
 class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
 
     @Autowired
@@ -155,17 +153,25 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Add list-node fragment with multiple elements.'() {
-        given: 'list node data fragment as a collection of data nodes'
+    def 'Add list-node fragment with multiple elements including an element with a child datanode.'() {
+        given: 'two new data nodes for an existing list'
             def listNodeXpaths = ['/parent-201/child-204[@key="B"]', '/parent-201/child-204[@key="C"]']
             def listNodeCollection = buildDataNodeCollection(listNodeXpaths)
-        when: 'list-node elements added to existing parent node'
+        and: 'a child node for one of the new data nodes'
+            def childDataNode = buildDataNode('/parent-201/child-204[@key="C"]/grand-child-204[@key2="Z"]', [leave:'value'], [])
+            listNodeCollection.iterator().next().childDataNodes = [childDataNode]
+        when: 'the data nodes (list elements) are added to existing parent node'
             objectUnderTest.addListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', listNodeCollection)
         then: 'new entries successfully persisted, parent node now contains 5 children (2 new + 3 existing before)'
             def parentFragment = fragmentRepository.getById(LIST_DATA_NODE_PARENT201_FRAGMENT_ID)
             def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() }
             assert allChildXpaths.size() == 5
             assert allChildXpaths.containsAll(listNodeXpaths)
+        and: 'the child node of the new list entry is also present'
+            def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME)
+            def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3)
+            def listElementChild = fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, childDataNode.xpath)
+            assert listElementChild.isPresent()
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
index 5ed3ae3..162a566 100644 (file)
 package org.onap.cps.spi.impl
 
 import org.hibernate.StaleStateException
+import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.entities.FragmentEntity
 import org.onap.cps.spi.exceptions.ConcurrencyException
+import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.model.DataNodeBuilder
 import org.onap.cps.spi.repository.AnchorRepository
 import org.onap.cps.spi.repository.DataspaceRepository
@@ -68,5 +70,43 @@ class CpsDataPersistenceServiceSpec extends Specification {
             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}}")
+            }
+        when: 'getting the data node represented by this fragment'
+            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
+        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
+    }
+
+    def 'Retrieving a data node with invalid JSON'() {
+        given: 'a fragment with 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',
+                'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+        then: 'a data validation exception is thrown'
+            thrown(DataValidationException)
+    }
 
 }
index 7a16a97..7e42200 100644 (file)
  */
 package org.onap.cps.spi.impl
 
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
-
 import org.onap.cps.spi.CpsAdminPersistenceService
 import org.onap.cps.spi.CpsModulePersistenceService
 import org.onap.cps.spi.entities.YangResourceEntity
-import org.onap.cps.spi.exceptions.DataspaceNotFoundException
 import org.onap.cps.spi.exceptions.AlreadyDefinedException
+import org.onap.cps.spi.exceptions.DataspaceNotFoundException
 import org.onap.cps.spi.exceptions.SchemaSetInUseException
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.spi.model.ModuleReference
+import org.onap.cps.spi.model.ExtendedModuleReference
 import org.onap.cps.spi.repository.AnchorRepository
 import org.onap.cps.spi.repository.SchemaSetRepository
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.test.context.jdbc.Sql
 
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
+
 class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
 
     @Autowired
@@ -71,13 +72,13 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase
     static final String NEW_RESOURCE_CHECKSUM = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539'
     static final String NEW_RESOURCE_MODULE_NAME = 'stores'
     static final String NEW_RESOURCE_REVISION = '2020-09-15'
-    static final ModuleReference newModuleReference = ModuleReference.builder().name(NEW_RESOURCE_MODULE_NAME)
+    static final ExtendedModuleReference newModuleReference = ExtendedModuleReference.builder().name(NEW_RESOURCE_MODULE_NAME)
             .revision(NEW_RESOURCE_REVISION).build()
 
     def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT]
-    def allYangResourcesModuleAndRevisionList = [ModuleReference.builder().build(),ModuleReference.builder().build(),
-                                                 ModuleReference.builder().build(),ModuleReference.builder().build(),
-                                                 ModuleReference.builder().build(), newModuleReference]
+    def allYangResourcesModuleAndRevisionList = [new ExtendedModuleReference(name: 'MODULE-NAME-002',namespace:null, revision: 'REVISION-002'), new ExtendedModuleReference(name: 'MODULE-NAME-003',namespace:null, revision: 'REVISION-003'),
+                                                 new ExtendedModuleReference(name: 'MODULE-NAME-004',namespace:null, revision: 'REVISION-004'), ExtendedModuleReference.builder().build(),
+                                                 ExtendedModuleReference.builder().build(), newModuleReference]
     def dataspaceEntity
 
     def setup() {
@@ -109,7 +110,7 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase
     def 'Store and retrieve new schema set from new modules and existing modules.'() {
         given: 'map of new modules, a list of existing modules, module reference'
             def mapOfNewModules = [newModule1: 'module newmodule { yang-version 1.1; revision "2021-10-12" { } }']
-            def moduleReferenceForExistingModule = new ModuleReference("test","test.org","2021-10-12")
+            def moduleReferenceForExistingModule = new ModuleReference("test","2021-10-12")
             def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
             def mapOfExistingModule = [test: 'module test { yang-version 1.1; revision "2021-10-12" { } }']
             objectUnderTest.storeSchemaSet(DATASPACE_NAME, "someSchemaSetName", mapOfExistingModule)
@@ -135,13 +136,27 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Retrieving all yang resources module references.'() {
-        given: 'a new schema set is stored'
-            objectUnderTest.storeSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NEW, newYangResourcesNameToContentMap)
-        when: 'all yang resources module references are retrieved'
-            def result = objectUnderTest.getAllYangResourcesModuleReferences()
+    def 'Retrieving all yang resources module references for the given dataspace.'() {
+        given: 'a dataspace name'
+            def dataspaceName = 'DATASPACE-002'
+        when: 'all yang resources module references are retrieved for the given dataspace name'
+            def result = objectUnderTest.getYangResourceModuleReferences(dataspaceName)
         then: 'the correct resources are returned'
-            result.sort() == allYangResourcesModuleAndRevisionList.sort()
+            result.sort() == [new ModuleReference(moduleName: 'MODULE-NAME-005', revision: 'REVISION-002'),
+                              new ModuleReference(moduleName: 'MODULE-NAME-006', revision: 'REVISION-006')]
+    }
+
+    @Sql([CLEAR_DATA, SET_DATA])
+    def 'Retrieving module names and revisions for the given anchor.'() {
+        given: 'a dataspace name and anchor name'
+            def dataspaceName = 'DATASPACE-001'
+            def anchorName = 'ANCHOR1'
+        when: 'all yang resources module references are retrieved for the given anchor'
+            def result = objectUnderTest.getYangResourceModuleReferences(dataspaceName, anchorName)
+        then: 'the correct module names and revisions are returned'
+            result.sort() == [new ModuleReference(moduleName: null, revision: null), new ModuleReference(moduleName: 'MODULE-NAME-002', revision: 'REVISION-002'),
+                              new ModuleReference(moduleName: 'MODULE-NAME-003', revision: 'REVISION-002'),
+                              new ModuleReference(moduleName: 'MODULE-NAME-004', revision: 'REVISION-004')]
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
index 5132632..8ec5c90 100644 (file)
@@ -55,6 +55,7 @@ class CpsPersistenceSpecBase extends Specification {
     static final String CLEAR_DATA = '/data/clear-all.sql'
 
     static final String DATASPACE_NAME = 'DATASPACE-001'
+    static final String DATASPACE_NAME2 = 'DATASPACE-002'
     static final String SCHEMA_SET_NAME1 = 'SCHEMA-SET-001'
     static final String SCHEMA_SET_NAME2 = 'SCHEMA-SET-002'
     static final String ANCHOR_NAME1 = 'ANCHOR-001'
index adfcfa1..6160035 100644 (file)
@@ -24,26 +24,32 @@ INSERT INTO DATASPACE (ID, NAME) VALUES
     (1001, 'DATASPACE-001'), (1002, 'DATASPACE-002');
 
 INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
-    (2001, 'SCHEMA-SET-001', 1001), (2002, 'SCHEMA-SET-002', 1001),
+    (2001, 'SCHEMA-SET-001', 1001),
+    (2002, 'SCHEMA-SET-002', 1001),
     (2100, 'SCHEMA-SET-100', 1001), -- for removal, not referenced by anchors
-    (2101, 'SCHEMA-SET-101', 1001); -- for removal, having anchor and data associated
-
-INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM) VALUES
-    (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'e8bdda931099310de66532e08c3fafec391db29f55c81927b168f6aa8f81b73b'),
-    (3002, 'module2@2020-02-02.yang', 'CONTENT-002', '7e7d48afbe066ed0a890a09081859046d3dde52300dfcdb13be5b20780353a11'),
-    (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'ca20c45fec8547633f05ff8905c48ffa7b02b94ec3ad4ed79922e6ba40779df3'),
-    (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'f6ed09d343562e4d4ae5140f3c6a55df9c53f6da8e30dda8cbd9eaf9cd449be0'),
-    (3100, 'orphan@2020-02-02.yang', 'ORPHAN', 'checksum'); -- for auto-removal as orphan
+    (2101, 'SCHEMA-SET-101', 1001), -- for removal, having anchor and data associated
+    (2003, 'SCHEMA-SET-003', 1002),
+    (2004, 'SCHEMA-SET-004', 1002);
+
+INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
+    (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'e8bdda931099310de66532e08c3fafec391db29f55c81927b168f6aa8f81b73b',null,null),
+    (3002, 'module2@2020-02-02.yang', 'CONTENT-002', '7e7d48afbe066ed0a890a09081859046d3dde52300dfcdb13be5b20780353a11','MODULE-NAME-002','REVISION-002'),
+    (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'ca20c45fec8547633f05ff8905c48ffa7b02b94ec3ad4ed79922e6ba40779df3','MODULE-NAME-003','REVISION-002'),
+    (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'f6ed09d343562e4d4ae5140f3c6a55df9c53f6da8e30dda8cbd9eaf9cd449be0','MODULE-NAME-004','REVISION-004'),
+    (3100, 'orphan@2020-02-02.yang', 'ORPHAN', 'checksum',null,null), -- for auto-removal as orphan
+    (3005, 'module5@2020-02-02.yang', 'CONTENT-005', 'checksum-005','MODULE-NAME-005','REVISION-002'),
+    (3006, 'module6@2020-02-02.yang', 'CONTENT-006', 'checksum-006','MODULE-NAME-006','REVISION-006');
 
 INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
     (2001, 3001), (2001, 3002),
     (2002, 3003), (2002, 3004),
     (2100, 3003), (2100, 3100), -- orphan removal case
-    (2101, 3003), (2101, 3004);
+    (2101, 3003), (2101, 3004),
+    (2003, 3005), (2004, 3006);
 
 INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES -- anchors for removal
     (6001, 'ANCHOR1', 1001, 2101),
     (6002, 'ANCHOR2', 1001, 2101);
 
 INSERT INTO FRAGMENT (ID, XPATH, ANCHOR_ID, DATASPACE_ID) VALUES
-    (7001, '/XPATH', 6001, 1001);
+    (7001, '/XPATH', 6001, 1001);
\ No newline at end of file
index c69ead0..c8daccc 100644 (file)
@@ -28,7 +28,7 @@
   <parent>\r
     <groupId>org.onap.cps</groupId>\r
     <artifactId>cps-parent</artifactId>\r
-    <version>1.1.0-SNAPSHOT</version>\r
+    <version>2.0.1-SNAPSHOT</version>\r
     <relativePath>../cps-parent/pom.xml</relativePath>\r
   </parent>\r
 \r
index 5c40331..1dccf49 100644 (file)
@@ -21,6 +21,7 @@
 
 package org.onap.cps.api;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import org.checkerframework.checker.nullness.qual.NonNull;
@@ -51,12 +52,12 @@ public interface CpsModuleService {
      * @param dataspaceName                          Dataspace name
      * @param schemaSetName                          schema set name
      * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content
-     * @param existingModuleReferences               List of YANG resources module references of the modules
+     * @param moduleReferences               List of YANG resources module references of the modules
      *                                               needed for this handle that are already in CPS
      */
     void createSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName,
                                     @NonNull Map<String, String> newYangResourcesModuleNameToContentMap,
-                                    @NonNull List<ModuleReference> existingModuleReferences);
+                                    @NonNull List<ModuleReference> moduleReferences);
 
     /**
      * Read schema set in the given dataspace.
@@ -80,9 +81,19 @@ public interface CpsModuleService {
         @NonNull CascadeDeleteAllowed cascadeDeleteAllowed);
 
     /**
-     * Retrieve all modules and revisions known by CPS for all Yang Resources.
+     * Retrieve module references for the given dataspace name.
      *
+     * @param dataspaceName        dataspace name
+     * @return a list of ModuleReference objects
+     */
+    Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName);
+
+    /**
+     * Retrieve module references for the given dataspace name and anchor name.
+     *
+     * @param dataspaceName dataspace name
+     * @param anchorName    anchor name
      * @return a list of ModuleReference objects
      */
-    List<ModuleReference> getAllYangResourcesModuleReferences();
+    Collection<ModuleReference> getYangResourcesModuleReferences(String dataspaceName, String anchorName);
 }
index 0597d38..1032641 100644 (file)
@@ -21,6 +21,7 @@
 
 package org.onap.cps.api.impl;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import org.onap.cps.api.CpsModuleService;
@@ -53,9 +54,9 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     @Override
     public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
                                            final Map<String, String> newYangResourcesModuleNameToContentMap,
-                                           final List<ModuleReference> existingModuleReferences) {
+                                           final List<ModuleReference> moduleReferences) {
         cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
-                newYangResourcesModuleNameToContentMap, existingModuleReferences);
+                newYangResourcesModuleNameToContentMap, moduleReferences);
 
     }
 
@@ -64,7 +65,7 @@ public class CpsModuleServiceImpl implements CpsModuleService {
         final var yangTextSchemaSourceSet = yangTextSchemaSourceSetCache
             .get(dataspaceName, schemaSetName);
         return SchemaSet.builder().name(schemaSetName).dataspaceName(dataspaceName)
-            .moduleReferences(yangTextSchemaSourceSet.getModuleReferences()).build();
+            .extendedModuleReferences(yangTextSchemaSourceSet.getModuleReferences()).build();
     }
 
     @Override
@@ -74,8 +75,13 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     }
 
     @Override
-    public List<ModuleReference> getAllYangResourcesModuleReferences() {
-        return cpsModulePersistenceService.getAllYangResourcesModuleReferences();
+    public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) {
+        return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName);
     }
 
+    @Override
+    public Collection<ModuleReference> getYangResourcesModuleReferences(final String dataspaceName,
+        final String anchorName) {
+        return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName, anchorName);
+    }
 }
index 4c96159..2667ef4 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.config;
 
 import javax.validation.constraints.Min;
 import lombok.Setter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -32,7 +33,8 @@ import org.springframework.validation.annotation.Validated;
 
 @EnableAsync
 @Configuration
-@ConfigurationProperties("notification.async-executor")
+@ConditionalOnProperty(name = "notification.async.enabled", havingValue = "true", matchIfMissing = false)
+@ConfigurationProperties("notification.async.executor")
 @Validated
 @Setter
 public class AsyncConfig {
index 7ad109d..9b50f9e 100755 (executable)
@@ -21,6 +21,7 @@
 
 package org.onap.cps.spi;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import org.checkerframework.checker.nullness.qual.NonNull;
@@ -48,11 +49,11 @@ public interface CpsModulePersistenceService {
      * @param dataspaceName                          Dataspace name
      * @param schemaSetName                          Schema set name
      * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content
-     * @param moduleReferenceList                    List of YANG resources module references
+     * @param moduleReferences                    List of YANG resources module references
      */
     void storeSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName,
                                    @NonNull Map<String, String> newYangResourcesModuleNameToContentMap,
-                                   @NonNull List<ModuleReference> moduleReferenceList);
+                                   @NonNull List<ModuleReference> moduleReferences);
 
     /**
      * Deletes Schema Set.
@@ -89,9 +90,19 @@ public interface CpsModulePersistenceService {
         @NonNull String anchorName);
 
     /**
-     * Returns all YANG resources module references.
+     * Returns YANG resources module references for the given dataspace name.
      *
-     * @return List of all YANG resources module information in the database
+     * @param dataspaceName dataspace name
+     * @return Collection of all YANG resources module information in the database
+     */
+    Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName);
+
+    /**
+     * Get YANG resource module references for the given anchor name and dataspace name.
+     *
+     * @param dataspaceName dataspace name
+     * @param anchorName    anchor name
+     * @return a collection of module names and revisions
      */
-    List<ModuleReference> getAllYangResourcesModuleReferences();
+    Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName, String anchorName);
 }
index 5773bc4..8e9dff8 100644 (file)
@@ -38,7 +38,7 @@ public class DataNode {
     private String dataspace;
     private String schemaSetName;
     private String anchorName;
-    private ModuleReference moduleReference;
+    private ExtendedModuleReference extendedModuleReference;
     private String xpath;
     private Map<String, Object> leaves = Collections.emptyMap();
     private Collection<String> xpathsChildren;
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/ExtendedModuleReference.java b/cps-service/src/main/java/org/onap/cps/spi/model/ExtendedModuleReference.java
new file mode 100644 (file)
index 0000000..5e9c8d0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * Modifications Copyright 2020-2021 Pantheon.tech
+ * ================================================================================
+ * 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.model;
+
+import java.io.Serializable;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ExtendedModuleReference implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String name;
+    private String namespace;
+    private String revision;
+
+}
index f9aa2b5..9b73f8f 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2020 Nordix Foundation.
- * Modifications Copyright 2020-2021 Pantheon.tech
+ * Copyright (C) 2021 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,10 +32,7 @@ import lombok.NoArgsConstructor;
 @AllArgsConstructor
 public class ModuleReference implements Serializable {
 
-    private static final long serialVersionUID = 1L;
-
-    private String name;
-    private String namespace;
+    private static final long serialVersionUID = -1761408847591042599L;
+    private String moduleName;
     private String revision;
-
 }
index caf7800..4df7893 100644 (file)
@@ -35,5 +35,5 @@ public class SchemaSet implements Serializable {
     private static final long serialVersionUID = 1464791260718603291L;
     private String name;
     private String dataspaceName;
-    private List<ModuleReference> moduleReferences;
+    private List<ExtendedModuleReference> extendedModuleReferences;
 }
index 32ee324..2c9d374 100644 (file)
@@ -21,7 +21,7 @@ package org.onap.cps.yang;
 
 import java.util.List;
 import org.checkerframework.checker.nullness.qual.NonNull;
-import org.onap.cps.spi.model.ModuleReference;
+import org.onap.cps.spi.model.ExtendedModuleReference;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
@@ -35,7 +35,7 @@ public interface YangTextSchemaSourceSet {
      * @return list of ModuleRef
      */
     @NonNull
-    List<ModuleReference> getModuleReferences();
+    List<ExtendedModuleReference> getModuleReferences();
 
     /**
      *  Return SchemaContext for given YangSchema.
index 3a65369..5cbfd62 100644 (file)
@@ -35,7 +35,7 @@ import java.util.stream.Collectors;
 import lombok.NoArgsConstructor;
 import org.onap.cps.spi.exceptions.CpsException;
 import org.onap.cps.spi.exceptions.ModelValidationException;
-import org.onap.cps.spi.model.ModuleReference;
+import org.onap.cps.spi.model.ExtendedModuleReference;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -88,14 +88,14 @@ public final class YangTextSchemaSourceSetBuilder {
         }
 
         @Override
-        public List<ModuleReference> getModuleReferences() {
+        public List<ExtendedModuleReference> getModuleReferences() {
             return schemaContext.getModules().stream()
                 .map(YangTextSchemaSourceSetImpl::toModuleReference)
                 .collect(Collectors.toList());
         }
 
-        private static ModuleReference toModuleReference(final Module module) {
-            return ModuleReference.builder()
+        private static ExtendedModuleReference toModuleReference(final Module module) {
+            return ExtendedModuleReference.builder()
                 .name(module.getName())
                 .namespace(module.getQNameModule().getNamespace().toString())
                 .revision(module.getRevision().map(Revision::toString).orElse(null))
index d719b3d..2c23aa1 100644 (file)
@@ -25,6 +25,7 @@ package org.onap.cps.api.impl
 import org.onap.cps.TestUtils
 import org.onap.cps.spi.CpsModulePersistenceService
 import org.onap.cps.spi.exceptions.ModelValidationException
+import org.onap.cps.spi.model.ExtendedModuleReference
 import org.onap.cps.spi.model.ModuleReference
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
@@ -63,7 +64,7 @@ class CpsModuleServiceImplSpec extends Specification {
 
     def 'Create schema set from new modules and existing modules.'() {
         given: 'a list of existing modules module reference'
-            def moduleReferenceForExistingModule = new ModuleReference("test", "test.org", "2021-10-12")
+            def moduleReferenceForExistingModule = new ExtendedModuleReference("test", "test.org", "2021-10-12")
             def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
         when: 'create schema set from modules method is invoked'
             objectUnderTest.createSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
@@ -90,7 +91,7 @@ class CpsModuleServiceImplSpec extends Specification {
         then: 'the correct schema set is returned'
             result.getName().contains('someSchemaSet')
             result.getDataspaceName().contains('someDataspace')
-            result.getModuleReferences().contains(new ModuleReference('stores', 'org:onap:ccsdk:sample', '2020-09-15'))
+            result.getExtendedModuleReferences().contains(new ExtendedModuleReference('stores', 'org:onap:ccsdk:sample', '2020-09-15'))
     }
 
     def 'Schema set caching.'() {
@@ -117,9 +118,18 @@ class CpsModuleServiceImplSpec extends Specification {
 
     def 'Get all yang resources module references.'(){
         given: 'an already present module reference'
+            def moduleReferences = [new ExtendedModuleReference()]
+            mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences
+        expect: 'the list provided by persistence service is returned as result'
+            objectUnderTest.getYangResourceModuleReferences('someDataspaceName') == moduleReferences
+    }
+
+
+    def 'Get all yang resources module references for the given dataspace name and anchor name.'(){
+        given: 'the module store service service returns a list module references'
             def moduleReferences = [new ModuleReference()]
-            mockModuleStoreService.getAllYangResourcesModuleReferences() >> moduleReferences
+            mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences
         expect: 'the list provided by persistence service is returned as result'
-            objectUnderTest.getAllYangResourcesModuleReferences() == moduleReferences
+            objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') == moduleReferences
     }
 }
index 875113d..ca704ed 100644 (file)
@@ -36,7 +36,6 @@ import spock.lang.Specification
 import java.util.concurrent.TimeUnit
 
 @SpringBootTest
-@EnableAsync
 @EnableConfigurationProperties
 @ContextConfiguration(classes = [NotificationProperties, NotificationService, NotificationErrorHandler, AsyncConfig])
 class NotificationServiceSpec extends Specification {
@@ -105,10 +104,4 @@ class NotificationServiceSpec extends Specification {
             1 * spyNotificationErrorHandler.onException(_, _, _, _)
     }
 
-    NotificationService createNotificationService(boolean notificationEnabled) {
-        spyNotificationProperties = Spy(notificationProperties)
-        spyNotificationProperties.isEnabled() >> notificationEnabled
-        return new NotificationService(spyNotificationProperties, mockNotificationPublisher,
-            mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler)
-    }
 }
index b1546d7..436c3d4 100644 (file)
@@ -22,10 +22,12 @@ notification:
       enabled-dataspaces: ".*-published,.*-important"
     enabled: true
     topic: cps-event
-  async-executor:
-    core-pool-size: 2
-    max-pool-size: 10
-    queue-capacity: 0
+  async:
+    enabled: true
+    executor:
+      core-pool-size: 2
+      max-pool-size: 10
+      queue-capacity: 0
 
 spring:
   kafka:
diff --git a/pom.xml b/pom.xml
index 8a89e6c..007032d 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>1.1.0-SNAPSHOT</version>\r
+    <version>2.0.1-SNAPSHOT</version>\r
     <packaging>pom</packaging>\r
 \r
     <name>cps</name>\r
diff --git a/releases/2.0.0-container.yaml b/releases/2.0.0-container.yaml
new file mode 100644 (file)
index 0000000..7021569
--- /dev/null
@@ -0,0 +1,9 @@
+distribution_type: container
+container_release_tag: 2.0.0
+project: cps
+log_dir: cps-maven-docker-stage-master/319/
+ref: d9b5bb295553f0adb2a09d341ad1e3f95c8c995d
+tag_release: true
+containers:
+  - name: 'cps-and-ncmp'
+    version: '2.0.0-20210913T194429Z'
diff --git a/releases/2.0.0.yaml b/releases/2.0.0.yaml
new file mode 100644 (file)
index 0000000..202467a
--- /dev/null
@@ -0,0 +1,5 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/319/
+project: cps
+version: 2.0.0
+tag_release: false
index 6b2d8c1..96bcac2 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>1.1.0-SNAPSHOT</version>
+    <version>2.0.1-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
index 04bd78a..67022d0 100755 (executable)
@@ -19,8 +19,8 @@
 # Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... )
 # because they are used in Jenkins, whose plug-in doesn't support this
 
-major=1
-minor=1
+major=2
+minor=0
 patch=0
 
 base_version=${major}.${minor}.${patch}