CPS-505 Retrieving modules for new CM handle 43/123543/16
authorJosephKeenan <joseph.keenan@est.tech>
Fri, 20 Aug 2021 09:33:54 +0000 (10:33 +0100)
committerJosephKeenan <joseph.keenan@est.tech>
Mon, 30 Aug 2021 16:38:03 +0000 (17:38 +0100)
-Added some production code for getting missing modules for new CM
handle
-Groovy test template added by Toine for getting msissing modules
-Added json example for test
-Modified test to check map contents
-Differentiated restTemplate calls based on URL
-Fixed code review comment`s
-Groovy test now passing
-Modified behaviour for sending moduleReferences and added null to
namespace (jira to follow)
-Combined NetworkCmProxyDataServiceImpl tests into one class & addressed
code review comments

Issue-ID: CPS-505

Change-Id: I91ef65467496caea7834ba2e8af99cfe58d4f880
Signed-off-by: JosephKeenan <joseph.keenan@est.tech>
16 files changed:
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/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.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/main/java/org/onap/cps/ncmp/api/models/YangResource.java [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/operation/DmiOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/TestUtils.java [new file with mode: 0644]
cps-ncmp-service/src/test/resources/cmHandleModules.json [new file with mode: 0644]
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/spi/model/ModuleReference.java

index d4e842a..f5ffdbe 100755 (executable)
@@ -109,7 +109,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         final @Valid RestDmiPluginRegistration restDmiPluginRegistration) {
         final DmiPluginRegistration dmiPluginRegistration =
             convertRestObjectToJavaApiObject(restDmiPluginRegistration);
-        networkCmProxyDataService.updateDmiPluginRegistration(dmiPluginRegistration);
+        networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration);
         return new ResponseEntity<>(HttpStatus.CREATED);
     }
 
index 1a2d3a2..65946a9 100644 (file)
@@ -191,7 +191,7 @@ class NetworkCmProxyControllerSpec extends Specification {
                 .content(jsonData)
             ).andReturn().response
         then: 'the cm handles are registered with the service'
-            1 * mockNetworkCmProxyDataService.updateDmiPluginRegistration(_)
+            1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(_)
         and: 'response status is created'
             response.status == HttpStatus.CREATED.value()
     }
index 03d70c1..0693f61 100644 (file)
@@ -105,7 +105,7 @@ public interface NetworkCmProxyDataService {
      *
      * @param dmiPluginRegistration Dmi Plugin Registration
      */
-    void updateDmiPluginRegistration(DmiPluginRegistration dmiPluginRegistration);
+    void updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
 
     /**
      * Get resource data for data store pass-through operational
index 11477ab..dfe5603 100755 (executable)
@@ -25,16 +25,23 @@ package org.onap.cps.ncmp.api.impl;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
 import java.time.OffsetDateTime;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import javax.validation.constraints.NotNull;
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.api.CpsQueryService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
@@ -44,10 +51,12 @@ import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
 import org.onap.cps.ncmp.api.models.GenericRequestBody;
 import org.onap.cps.ncmp.api.models.PersistenceCmHandle;
 import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList;
+import org.onap.cps.ncmp.api.models.YangResource;
 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;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
@@ -75,6 +84,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private DmiOperations dmiOperations;
 
+    private CpsModuleService cpsModuleService;
+
+    private CpsAdminService cpsAdminService;
+
+    public static final String NO_NAMESPACE = null;
+
     /**
      * Constructor Injection for Dependencies.
      * @param dmiOperations DMI operation
@@ -82,58 +97,60 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
      * @param cpsQueryService Query Service Interface
      * @param objectMapper Object Mapper
      */
-    public NetworkCmProxyDataServiceImpl(final DmiOperations dmiOperations, final CpsDataService cpsDataService,
-                                         final CpsQueryService cpsQueryService, final ObjectMapper objectMapper) {
+    public NetworkCmProxyDataServiceImpl(final DmiOperations dmiOperations,
+        final CpsModuleService cpsModuleService,
+        final CpsDataService cpsDataService,
+        final CpsQueryService cpsQueryService,
+        final CpsAdminService cpsAdminService,
+        final ObjectMapper objectMapper) {
         this.dmiOperations = dmiOperations;
+        this.cpsModuleService = cpsModuleService;
         this.cpsDataService = cpsDataService;
         this.cpsQueryService = cpsQueryService;
+        this.cpsAdminService = cpsAdminService;
         this.objectMapper = objectMapper;
     }
 
-    private String getDataspaceName() {
-        return NF_PROXY_DATASPACE_NAME;
-    }
-
     @Override
     public DataNode getDataNode(final String cmHandle, final String xpath,
-                                final FetchDescendantsOption fetchDescendantsOption) {
-        return cpsDataService.getDataNode(getDataspaceName(), cmHandle, xpath, fetchDescendantsOption);
+        final FetchDescendantsOption fetchDescendantsOption) {
+        return cpsDataService.getDataNode(NF_PROXY_DATASPACE_NAME, cmHandle, xpath, fetchDescendantsOption);
     }
 
     @Override
     public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath,
-                                               final FetchDescendantsOption fetchDescendantsOption) {
-        return cpsQueryService.queryDataNodes(getDataspaceName(), cmHandle, cpsPath, fetchDescendantsOption);
+        final FetchDescendantsOption fetchDescendantsOption) {
+        return cpsQueryService.queryDataNodes(NF_PROXY_DATASPACE_NAME, cmHandle, cpsPath, fetchDescendantsOption);
     }
 
     @Override
     public void createDataNode(final String cmHandle, final String parentNodeXpath, final String jsonData) {
         if (!StringUtils.hasText(parentNodeXpath) || "/".equals(parentNodeXpath)) {
-            cpsDataService.saveData(getDataspaceName(), cmHandle, jsonData, NO_TIMESTAMP);
+            cpsDataService.saveData(NF_PROXY_DATASPACE_NAME, cmHandle, jsonData, NO_TIMESTAMP);
         } else {
-            cpsDataService.saveData(getDataspaceName(), cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
+            cpsDataService.saveData(NF_PROXY_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
         }
     }
 
     @Override
     public void addListNodeElements(final String cmHandle, final String parentNodeXpath, final String jsonData) {
-        cpsDataService.saveListNodeData(getDataspaceName(), cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
+        cpsDataService.saveListNodeData(NF_PROXY_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
     }
 
     @Override
     public void updateNodeLeaves(final String cmHandle, final String parentNodeXpath, final String jsonData) {
-        cpsDataService.updateNodeLeaves(getDataspaceName(), cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
+        cpsDataService.updateNodeLeaves(NF_PROXY_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
     }
 
     @Override
     public void replaceNodeTree(final String cmHandle, final String parentNodeXpath, final String jsonData) {
-        cpsDataService.replaceNodeTree(getDataspaceName(), cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
+        cpsDataService.replaceNodeTree(NF_PROXY_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
     }
 
     @Override
-    public void updateDmiPluginRegistration(final DmiPluginRegistration dmiPluginRegistration) {
+    public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) {
         if (dmiPluginRegistration.getCreatedCmHandles() != null) {
-            parseAndCreateCmHandlesInDmiRegistration(dmiPluginRegistration);
+            parseAndCreateCmHandlesInDmiRegistrationAndSyncModule(dmiPluginRegistration);
         }
         if (dmiPluginRegistration.getUpdatedCmHandles() != null) {
             parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration);
@@ -218,10 +235,10 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private String prepareOperationBody(final GenericRequestBody requestBodyObject) {
         try {
             return objectMapper.writeValueAsString(requestBodyObject);
-        } catch (final JsonProcessingException je) {
+        } catch (final JsonProcessingException e) {
             log.error("Parsing error occurred while converting Object to JSON.");
             throw new NcmpException("Parsing error occurred while converting given object to JSON.",
-                    je.getMessage());
+                e.getMessage());
         }
     }
 
@@ -265,67 +282,132 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return prepareOperationBody(requetBodyObject);
     }
 
-    private 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);
-        } else {
-            persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties());
+    private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
+        try {
+            final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList();
+
+            for (final CmHandle cmHandle : dmiPluginRegistration.getUpdatedCmHandles()) {
+                final PersistenceCmHandle persistenceCmHandle =
+                    toPersistenceCmHandle(dmiPluginRegistration.getDmiPlugin(), cmHandle);
+                persistenceCmHandlesList.add(persistenceCmHandle);
+            }
+            final String cmHandlesJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
+            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);
         }
-        return persistenceCmHandle;
     }
 
-    private void parseAndCreateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
+    private void parseAndCreateCmHandlesInDmiRegistrationAndSyncModule(
+        final DmiPluginRegistration dmiPluginRegistration) {
         try {
-            final List<PersistenceCmHandle> createdPersistenceCmHandles =
-                    new LinkedList<>();
-            for (final CmHandle cmHandle: dmiPluginRegistration.getCreatedCmHandles()) {
-                createdPersistenceCmHandles.add(toPersistenceCmHandle(dmiPluginRegistration.getDmiPlugin(), cmHandle));
+            final var persistenceCmHandlesList = new PersistenceCmHandlesList();
+            for (final CmHandle cmHandle : dmiPluginRegistration.getCreatedCmHandles()) {
+                final PersistenceCmHandle persistenceCmHandle =
+                    toPersistenceCmHandle(dmiPluginRegistration.getDmiPlugin(), cmHandle);
+                persistenceCmHandlesList.add(persistenceCmHandle);
+                createAnchorAndSyncModel(persistenceCmHandle);
             }
-            final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList();
-            persistenceCmHandlesList.setCmHandles(createdPersistenceCmHandles);
             final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
             cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry",
-                    cmHandleJsonData, NO_TIMESTAMP);
+                cmHandleJsonData, NO_TIMESTAMP);
         } 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);
+                "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e
+                .getMessage(), e);
         }
     }
 
-    private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
-        try {
-            final List<PersistenceCmHandle> updatedPersistenceCmHandles =
-                    new LinkedList<>();
-            for (final CmHandle cmHandle: dmiPluginRegistration.getUpdatedCmHandles()) {
-                updatedPersistenceCmHandles.add(toPersistenceCmHandle(dmiPluginRegistration.getDmiPlugin(), cmHandle));
-            }
-            final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList();
-            persistenceCmHandlesList.setCmHandles(updatedPersistenceCmHandles);
-            final String cmHandlesJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
-            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);
+    private 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);
+        } else {
+            persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties());
         }
+        return persistenceCmHandle;
     }
 
     private void parseAndRemoveCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
-        for (final String cmHandle: dmiPluginRegistration.getRemovedCmHandles()) {
+        for (final String cmHandle : dmiPluginRegistration.getRemovedCmHandles()) {
             try {
                 cpsDataService.deleteListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                        "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
+                    "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
             } catch (final DataNodeNotFoundException e) {
                 log.warn("Datanode {} not deleted message {}", cmHandle, e.getMessage());
             }
         }
     }
+
+    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> existingModuleReferences = new ArrayList<>();
+        for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromDmiForCmHandle) {
+            if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) {
+                existingModuleReferences.add(moduleReferenceFromDmiForCmHandle);
+            }
+        }
+
+        final Map<String, String> newYangResourcesModuleNameToContentMap =
+            getNewYangResources(cmHandle);
+
+        cpsModuleService.createSchemaSetFromModules(NCMP_DATASPACE_NAME, cmHandle.getId(),
+            newYangResourcesModuleNameToContentMap, existingModuleReferences);
+
+        cpsAdminService.createAnchor(NCMP_DATASPACE_NAME, cmHandle.getId(), cmHandle.getId());
+    }
+
+    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);
+        final Map<String, String> newYangResourcesModuleNameToContentMap = new HashMap<>();
+
+        for (final JsonElement moduleResource : moduleResources) {
+            final YangResource yangResource = toYangResource((JsonObject) moduleResource);
+            newYangResourcesModuleNameToContentMap.put(yangResource.getModuleName(), yangResource.getYangSource());
+        }
+        return newYangResourcesModuleNameToContentMap;
+    }
+
+    private 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());
+        return yangResource;
+    }
+
+    private 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);
+            modulesFromDmiForCmHandle.add(moduleReference);
+        }
+        return modulesFromDmiForCmHandle;
+    }
+
+    private ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) {
+        final var moduleReference = new ModuleReference();
+        moduleReference.setName(moduleReferenceAsJson.get("moduleName").getAsString());
+        moduleReference.setNamespace(NO_NAMESPACE);
+        moduleReference.setRevision(moduleReferenceAsJson.get("revision").getAsString());
+        return moduleReference;
+    }
 }
index cc4e2c7..af691f6 100644 (file)
@@ -57,4 +57,9 @@ public class DmiRestClient {
         httpHeaders.setContentType(MediaType.APPLICATION_JSON);
         return httpHeaders;
     }
+
+    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);
+    }
 }
\ No newline at end of file
index 63c4d49..8896b9f 100644 (file)
@@ -30,7 +30,6 @@ import org.springframework.stereotype.Component;
 
 @Component
 public class DmiOperations {
-
     @Getter
     public enum DataStoreEnum {
         PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"),
@@ -49,9 +48,9 @@ public class DmiOperations {
     }
 
     private DmiRestClient dmiRestClient;
-    private static final String DMI_BASE_PATH = "/dmi/api";
-    private static final String PARENT_CM_HANDLE_URI =
-            "/v1/ch/{cmHandle}/data/ds";
+    private static final String DMI_API_PATH = "/dmi/api";
+    private static final String DMI_CM_HANDLE_PATH = "/v1/ch/{cmHandle}";
+    private static final String DMI_CM_HANDLE_DATASTORE_PATH = DMI_CM_HANDLE_PATH + "/data/ds";
     private static final String URL_SEPARATOR = "/";
 
     /**
@@ -63,28 +62,45 @@ public class DmiOperations {
         this.dmiRestClient = dmiRestClient;
     }
 
+    /**
+     * Get resources from DMI.
+     *
+     * @param dmiServiceName dmi base path
+     * @param cmHandle cmHandle
+     * @param resourceName name of the resource(s)
+     * @return {@code ResponseEntity} response entity
+     */
+    public ResponseEntity<String> getResourceFromDmi(final String dmiServiceName,
+                                                     final String cmHandle,
+                                                     final String resourceName) {
+        final var dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
+        final var httpHeaders = new HttpHeaders();
+        return dmiRestClient.postOperation(dmiResourceDataUrl, httpHeaders);
+
+    }
+
     /**
      * This method fetches the resource data from operational data store for given cm handle
      * identifier on given resource using dmi client.
      *
-     * @param dmiBasePath dmi base path
-     * @param cmHandle network resource identifier
-     * @param resourceId resource identifier
+     * @param dmiServiceName dmi service name
+     * @param cmHandle    network resource identifier
+     * @param resourceId  resource identifier
      * @param fieldsQuery fields query
-     * @param depthQuery depth query
+     * @param depthQuery  depth query
      * @param acceptParam accept parameter
-     * @param jsonBody json body for put operation
+     * @param jsonBody    json body for put operation
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<Object> getResourceDataOperationalFromDmi(final String dmiBasePath,
+    public ResponseEntity<Object> getResourceDataOperationalFromDmi(final String dmiServiceName,
                                                                     final String cmHandle,
                                                                     final String resourceId,
                                                                     final String fieldsQuery,
                                                                     final Integer depthQuery,
                                                                     final String acceptParam,
                                                                     final String jsonBody) {
-        final var dmiResourceDataUrl = getDmiResourceDataUrl(dmiBasePath, cmHandle, resourceId,
-                fieldsQuery, depthQuery, DataStoreEnum.PASSTHROUGH_OPERATIONAL);
+        final var dmiResourceDataUrl = getDmiDatastoreUrl(dmiServiceName, cmHandle, resourceId,
+            fieldsQuery, depthQuery, DataStoreEnum.PASSTHROUGH_OPERATIONAL);
         final var httpHeaders = prepareHeader(acceptParam);
         return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders);
     }
@@ -93,24 +109,24 @@ public class DmiOperations {
      * This method fetches the resource data from pass-through running data store for given cm handle
      * identifier on given resource using dmi client.
      *
-     * @param dmiBasePath dmi base path
-     * @param cmHandle network resource identifier
-     * @param resourceId resource identifier
+     * @param dmiServiceName dmi service name
+     * @param cmHandle    network resource identifier
+     * @param resourceId  resource identifier
      * @param fieldsQuery fields query
-     * @param depthQuery depth query
+     * @param depthQuery  depth query
      * @param acceptParam accept parameter
-     * @param jsonBody json body for put operation
+     * @param jsonBody    json body for put operation
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<Object> getResourceDataPassThroughRunningFromDmi(final String dmiBasePath,
+    public ResponseEntity<Object> getResourceDataPassThroughRunningFromDmi(final String dmiServiceName,
                                                                            final String cmHandle,
                                                                            final String resourceId,
                                                                            final String fieldsQuery,
                                                                            final Integer depthQuery,
                                                                            final String acceptParam,
                                                                            final String jsonBody) {
-        final var dmiResourceDataUrl = getDmiResourceDataUrl(dmiBasePath, cmHandle, resourceId,
-                fieldsQuery, depthQuery, DataStoreEnum.PASSTHROUGH_RUNNING);
+        final var dmiResourceDataUrl = getDmiDatastoreUrl(dmiServiceName, cmHandle, resourceId,
+            fieldsQuery, depthQuery, DataStoreEnum.PASSTHROUGH_RUNNING);
         final var httpHeaders = prepareHeader(acceptParam);
         return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders);
     }
@@ -120,9 +136,9 @@ public class DmiOperations {
      * identifier on given resource using dmi client.
      *
      * @param dmiBasePath dmi base path
-     * @param cmHandle network resource identifier
-     * @param resourceId resource identifier
-     * @param jsonBody json body for put operation
+     * @param cmHandle    network resource identifier
+     * @param resourceId  resource identifier
+     * @param jsonBody    json body for put operation
      * @return {@code ResponseEntity} response entity
      */
     public ResponseEntity<Void> createResourceDataPassThroughRunningFromDmi(final String dmiBasePath,
@@ -130,19 +146,30 @@ public class DmiOperations {
                                                                             final String resourceId,
                                                                             final String jsonBody) {
         final var stringBuilder = getStringBuilderForPassThroughRunningUrl(dmiBasePath,
-                cmHandle, resourceId, DataStoreEnum.PASSTHROUGH_RUNNING);
+            cmHandle, resourceId, DataStoreEnum.PASSTHROUGH_RUNNING);
         return dmiRestClient.postOperationWithJsonData(stringBuilder.toString(), jsonBody, new HttpHeaders());
     }
 
     @NotNull
-    private String getDmiResourceDataUrl(final String dmiBasePath,
-                                                final String cmHandle,
-                                                final String resourceId,
-                                                final String fieldsQuery,
-                                                final Integer depthQuery,
-                                                final DataStoreEnum dataStoreEnum) {
-        final var stringBuilder = getStringBuilderForPassThroughRunningUrl(dmiBasePath,
-                cmHandle, resourceId, dataStoreEnum);
+    private String getDmiResourceUrl(final String dmiServiceName,
+                                     final String cmHandle,
+                                     final String resourceName) {
+        final var stringBuilder = new StringBuilder(dmiServiceName);
+        stringBuilder.append(DMI_API_PATH);
+        stringBuilder.append(DMI_CM_HANDLE_PATH.replace("{cmHandle}", cmHandle));
+        stringBuilder.append(URL_SEPARATOR + resourceName);
+        return stringBuilder.toString();
+    }
+
+    @NotNull
+    private String getDmiDatastoreUrl(final String dmiServiceName,
+                                      final String cmHandle,
+                                      final String resourceId,
+                                      final String fieldsQuery,
+                                      final Integer depthQuery,
+                                      final DataStoreEnum dataStoreEnum) {
+        final var stringBuilder = getStringBuilderForPassThroughRunningUrl(dmiServiceName,
+            cmHandle, resourceId, dataStoreEnum);
         appendFieldsAndDepth(stringBuilder, fieldsQuery, depthQuery);
         return stringBuilder.toString();
     }
@@ -152,11 +179,11 @@ public class DmiOperations {
                                                                    final String cmHandle,
                                                                    final String resourceId,
                                                                    final DataStoreEnum dataStoreEnum) {
-        final var stringBuilder =  new StringBuilder(dmiServiceName);
-        stringBuilder.append(DMI_BASE_PATH);
-        stringBuilder.append(PARENT_CM_HANDLE_URI.replace("{cmHandle}", cmHandle));
+        final var stringBuilder = new StringBuilder(dmiServiceName);
+        stringBuilder.append(DMI_API_PATH);
+        stringBuilder.append(DMI_CM_HANDLE_DATASTORE_PATH.replace("{cmHandle}", cmHandle));
         stringBuilder.append(URL_SEPARATOR + dataStoreEnum.getValue());
-        stringBuilder.insert(stringBuilder.length(), URL_SEPARATOR + resourceId);
+        stringBuilder.append(URL_SEPARATOR + resourceId);
         return stringBuilder;
     }
 
index 95e8515..beeb00f 100644 (file)
@@ -27,10 +27,18 @@ import java.util.List;
 public class PersistenceCmHandlesList {
 
     @JsonProperty("cm-handles")
-    private List<PersistenceCmHandle> cmHandles;
+    private List<PersistenceCmHandle> persistenceCmHandles;
 
-    public void setCmHandles(final List<PersistenceCmHandle> cmHandlesAsList) {
-        cmHandles = new ArrayList<>(cmHandlesAsList);
+    /**
+     * Add a persistenceCmHandle.
+     *
+     * @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/main/java/org/onap/cps/ncmp/api/models/YangResource.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/YangResource.java
new file mode 100644 (file)
index 0000000..7975777
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  ============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 lombok.Data;
+
+@Data
+public class YangResource {
+
+    private String moduleName;
+
+    private String revision;
+
+    private String yangSource;
+
+}
index 8a32ad5..b42db57 100644 (file)
@@ -24,14 +24,20 @@ package org.onap.cps.ncmp.api.impl
 
 import com.fasterxml.jackson.core.JsonProcessingException
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
+import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.CpsQueryService
+import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
 import org.onap.cps.ncmp.api.impl.exception.NcmpException
 import org.onap.cps.ncmp.api.impl.operation.DmiOperations
 import org.onap.cps.ncmp.api.models.CmHandle
 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.model.DataNode
+import org.onap.cps.spi.model.ModuleReference
 import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import spock.lang.Shared
@@ -47,11 +53,19 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def mockCpsDataService = Mock(CpsDataService)
     def mockCpsQueryService = Mock(CpsQueryService)
     def mockDmiOperations = Mock(DmiOperations)
-    def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsDataService, mockCpsQueryService, new ObjectMapper())
+    def mockCpsModuleService = Mock(CpsModuleService)
+    def mockCpsAdminService = Mock(CpsAdminService)
+    def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties)
+
+    def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
+            mockCpsDataService, mockCpsQueryService, mockCpsAdminService, new ObjectMapper())
 
     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.'() {
@@ -64,6 +78,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         where: 'all fetch descendants options are supported'
             fetchDescendantsOption << FetchDescendantsOption.values()
     }
+
     def 'Create full data node: #scenario.'() {
         given: 'a cm handle and root xpath'
             def jsonData = 'some json'
@@ -76,6 +91,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             'no xpath'         | ''
             'root level xpath' | '/'
     }
+
     def 'Create child data node.'() {
         given: 'a cm handle and parent node xpath'
             def jsonData = 'some json'
@@ -85,6 +101,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         then: 'the CPS service method is invoked once with the expected parameters'
             1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
     }
+
     def 'Add list-node elements.'() {
         given: 'a cm handle and parent node xpath'
             def jsonData = 'some json'
@@ -94,6 +111,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         then: 'the CPS service method is invoked once with the expected parameters'
             1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
     }
+
     def 'Update data node leaves.'() {
         given: 'a cm Handle and a cps path'
             def xpath = '/xpath'
@@ -103,6 +121,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         then: 'the persistence service is called once with the correct parameters'
             1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
     }
+
     def 'Replace data node tree.'() {
         given: 'a cm Handle and a cps path'
             def xpath = '/xpath'
@@ -115,6 +134,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
         given: 'a registration '
+            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
             def dmiPluginRegistration = new DmiPluginRegistration()
             dmiPluginRegistration.dmiPlugin = 'my-server'
             persistenceCmHandle.cmHandleID = '123'
@@ -124,7 +144,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             dmiPluginRegistration.removedCmHandles = removedCmHandles
             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
         when: 'registration is updated'
-            objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'the CPS save list node data is invoked with the expected parameters'
             expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
                 '/dmi-registry', expectedJsonData, noTimestamp)
@@ -146,6 +166,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
         given: 'a registration without cmHandle properties '
+            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
             def dmiPluginRegistration = new DmiPluginRegistration()
             dmiPluginRegistration.dmiPlugin = 'my-server'
             persistenceCmHandle.cmHandleID = '123'
@@ -153,7 +174,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
         when: 'registration is updated'
-            objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'the CPS save list node data is invoked with the expected parameters'
             1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
                 '/dmi-registry', expectedJsonData, noTimestamp)
@@ -161,7 +182,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Get resource data for pass-through operational from dmi.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         when: 'get resource data is called'
             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
             'testResourceId',
@@ -186,7 +207,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cps data service returns valid cmHandle data node'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -206,7 +227,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cps data service returns valid cmHandle data node'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -231,7 +252,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Get resource data for pass-through running from dmi.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cpsDataService returns valid dataNode'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -255,7 +276,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cpsDataService returns valid dataNode'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -275,7 +296,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Get resource data for pass-through running from dmi return NOK response.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cpsDataService returns valid dataNode'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -300,7 +321,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Write resource data for pass-through running from dmi using POST.'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cpsDataService returns valid dataNode'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -318,7 +339,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
         given: 'data node representing cmHandle and its properties'
-            def cmHandleDataNode = prepareCmHandleDataNode()
+            def cmHandleDataNode = getCmHandleDataNodeForTest()
         and: 'cpsDataService returns valid dataNode'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
@@ -333,12 +354,51 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             thrown(NcmpException.class)
     }
 
-    private DataNode prepareCmHandleDataNode() {
+    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')
+        and: 'CPS-Core returns list of known modules'
+            mockCpsModuleService.getAllYangResourcesModuleReferences() >> [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
+        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])
+        and: 'admin service create anchor method has been called with correct parameters'
+            1 * mockCpsAdminService.createAnchor(expectedDataspaceNameForModleSync, 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'  | '[]'                                                                        || [:]
+    }
+
+    def getModulesForCmHandle() {
+        def jsonData = TestUtils.getResourceFileContent('cmHandleModules.json')
+        mockDmiProperties.getAuthUsername() >> 'someUser'
+        mockDmiProperties.getAuthPassword() >> 'somePassword'
+        mockDmiProperties.getDmiPluginBasePath() >> 'someUrl'
+        def moduleReferencesFromCmHandleAsJson = new ResponseEntity<String>(jsonData, HttpStatus.OK)
+        mockDmiOperations.getResourceFromDmi(_, cmHandleForModelSync.getId(), 'modules') >> moduleReferencesFromCmHandleAsJson
+    }
+
+    def getObjectUnderTestWithModelSyncDisabled() {
+        def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
+                mockCpsDataService, mockCpsQueryService, mockCpsAdminService, new ObjectMapper()))
+        objectUnderTest.createAnchorAndSyncModel(_) >> null
+        return objectUnderTest
+    }
+
+    def getCmHandleDataNodeForTest() {
         def cmHandleDataNode = new DataNode()
         cmHandleDataNode.leaves = ['dmi-service-name': 'testDmiService']
         def cmHandlePropertyDataNode = new DataNode()
         cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
         cmHandleDataNode.childDataNodes = [cmHandlePropertyDataNode]
-        cmHandleDataNode
+        return cmHandleDataNode
     }
+
 }
index c2a135e..3c9b164 100644 (file)
@@ -41,44 +41,55 @@ class DmiOperationsSpec extends Specification {
 
     def 'call get resource data for pass-through:operational datastore from DMI.'() {
         given: 'expected url'
-            def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/data/ds' +
-                    '/ncmp-datastore:passthrough-operational/testResourceId?fields=testFieldsQuery&depth=10'
+        def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/data/ds' +
+                '/ncmp-datastore:passthrough-operational/testResourceId?fields=testFieldsQuery&depth=10'
         when: 'get resource data is called to DMI'
-            objectUnderTest.getResourceDataOperationalFromDmi('testDmiBasePath',
-                    'testCmhandle',
-                    'testResourceId',
-                    'testFieldsQuery',
-                    10,
-                    'testAcceptJson',
-                    'testJsonbody')
+        objectUnderTest.getResourceDataOperationalFromDmi('testDmiBasePath',
+                'testCmhandle',
+                'testResourceId',
+                'testFieldsQuery',
+                10,
+                'testAcceptJson',
+                'testJsonbody')
         then: 'the put operation is executed with the correct URL'
-            1 * mockDmiRestClient.putOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
+        1 * mockDmiRestClient.putOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
     }
     def 'call get resource data for pass-through:running datastore from DMI.'() {
         given: 'expected url'
-            def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/data/ds' +
-                    '/ncmp-datastore:passthrough-running/testResourceId?fields=testFieldsQuery&depth=10'
+        def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/data/ds' +
+                '/ncmp-datastore:passthrough-running/testResourceId?fields=testFieldsQuery&depth=10'
         when: 'get resource data is called to DMI'
-            objectUnderTest.getResourceDataPassThroughRunningFromDmi('testDmiBasePath',
-                    'testCmhandle',
-                    'testResourceId',
-                    'testFieldsQuery',
-                    10,
-                    'testAcceptJson',
-                    'testJsonbody')
+        objectUnderTest.getResourceDataPassThroughRunningFromDmi('testDmiBasePath',
+                'testCmhandle',
+                'testResourceId',
+                'testFieldsQuery',
+                10,
+                'testAcceptJson',
+                'testJsonbody')
         then: 'the put operation is executed with the correct URL'
-            1 * mockDmiRestClient.putOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
+        1 * mockDmiRestClient.putOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
     }
     def 'call create resource data for pass-through:running datastore from DMI.'() {
         given: 'expected url'
-            def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/data/ds' +
-                    '/ncmp-datastore:passthrough-running/testResourceId'
+        def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/data/ds' +
+                '/ncmp-datastore:passthrough-running/testResourceId'
         when: 'get resource data is called to DMI'
-            objectUnderTest.createResourceDataPassThroughRunningFromDmi('testDmiBasePath',
-                    'testCmhandle',
-                    'testResourceId',
-                    'testJsonbody')
+        objectUnderTest.createResourceDataPassThroughRunningFromDmi('testDmiBasePath',
+                'testCmhandle',
+                'testResourceId',
+                'testJsonbody')
         then: 'the put operation is executed with the correct URL'
-            1 * mockDmiRestClient.postOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
+        1 * mockDmiRestClient.postOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
+    }
+
+    def 'Call get resource from dmi.'() {
+        given: 'expected url'
+            def expectedUrl = 'testDmiBasePath/dmi/api/v1/ch/testCmhandle/modules'
+        when: 'get resource data is called to dmi'
+            objectUnderTest.getResourceFromDmi('testDmiBasePath',
+                    'testCmhandle',
+                    'modules')
+        then: 'the post operation is executed with the correct URL'
+            1 * mockDmiRestClient.postOperation(expectedUrl, _ as HttpHeaders)
     }
 }
\ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy
new file mode 100644 (file)
index 0000000..d6cb463
--- /dev/null
@@ -0,0 +1,20 @@
+package org.onap.cps.ncmp.api.models
+
+import org.onap.cps.spi.model.ModuleReference
+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')
+        when: 'lombok generated methods are called'
+        then: 'the methods exist and behaviour is accurate'
+            assert moduleReference1.toString() == moduleReference2.toString()
+            assert moduleReference1.hashCode() == moduleReference2.hashCode()
+        and: 'therefore equals works as expected'
+            assert moduleReference1.equals(moduleReference2)
+    }
+
+}
diff --git a/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/TestUtils.java b/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/TestUtils.java
new file mode 100644 (file)
index 0000000..06ee6ed
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * ============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.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+/**
+ * Common convenience methods for testing.
+ */
+public class TestUtils {
+
+    /**
+     * Convert a file in the test resource folder to file.
+     *
+     * @param filename to name of the file in test/resources
+     * @return the file
+     * @throws IOException when there is an IO issue
+     */
+    public static File readFile(final String filename) {
+        return new File(ClassLoader.getSystemClassLoader().getResource(filename).getFile());
+    }
+
+    /**
+     * Convert a file in the test resource folder to a string.
+     *
+     * @param filename to name of the file in test/resources
+     * @return the content of the file as a String
+     * @throws IOException when there is an IO issue
+     */
+    public static String getResourceFileContent(final String filename) throws IOException {
+        final File file = readFile(filename);
+        return new String(Files.readAllBytes(file.toPath()));
+    }
+}
diff --git a/cps-ncmp-service/src/test/resources/cmHandleModules.json b/cps-ncmp-service/src/test/resources/cmHandleModules.json
new file mode 100644 (file)
index 0000000..d1665be
--- /dev/null
@@ -0,0 +1,12 @@
+{   "schemas": [
+  {
+    "moduleName": "module1",
+    "revision": "1",
+    "namespace": "some namespace"
+  },
+  {
+    "moduleName": "module2",
+    "revision": "1",
+    "namespace": "some namespace"
+  }]
+}
\ No newline at end of file
index d174085..5c40331 100644 (file)
@@ -51,11 +51,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 moduleReferenceList                    List of YANG resources module references
+     * @param existingModuleReferences               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> moduleReferenceList);
+                                    @NonNull List<ModuleReference> existingModuleReferences);
 
     /**
      * Read schema set in the given dataspace.
index c65f827..0597d38 100644 (file)
@@ -53,9 +53,9 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     @Override
     public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
                                            final Map<String, String> newYangResourcesModuleNameToContentMap,
-                                           final List<ModuleReference> moduleReferenceList) {
+                                           final List<ModuleReference> existingModuleReferences) {
         cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
-                newYangResourcesModuleNameToContentMap, moduleReferenceList);
+                newYangResourcesModuleNameToContentMap, existingModuleReferences);
 
     }
 
index 6aa68cf..f9aa2b5 100644 (file)
@@ -38,4 +38,5 @@ public class ModuleReference implements Serializable {
     private String name;
     private String namespace;
     private String revision;
+
 }