summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorniamhcore <niamh.core@est.tech>2021-07-30 16:25:16 +0100
committerniamhcore <niamh.core@est.tech>2021-08-10 13:33:52 +0100
commit3139ece993c68ce7e40d166acdd5d9572a3a8a1e (patch)
tree4a9ee12234815d6f5bc14557d5228c53b66f3b81
parent2270d76e4f33ad231cdae317e88ea1769297cfec (diff)
Retrieve yang-resources for one or more modules
Updating openapi to add a new rest endpoint Updating restconf client to support post with json Adding a ModuleResourceNotFound exception Adding a test util class Fixing merge conflict Refactoring SDNC operations Issue-ID: CPS-484 Signed-off-by: niamhcore <niamh.core@est.tech> Change-Id: Id76dfe4cb12053771883e0271153d7bf7cd98548
-rw-r--r--docs/openapi/components.yml33
-rw-r--r--docs/openapi/openapi.yml27
-rw-r--r--pom.xml4
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java2
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java38
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/model/ModuleReference.java37
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java32
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java9
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java56
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java27
-rw-r--r--src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java45
-rw-r--r--src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy52
-rw-r--r--src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy44
-rw-r--r--src/test/groovy/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClientSpec.groovy33
-rw-r--r--src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy31
-rw-r--r--src/test/java/org/onap/cps/ncmp/dmi/TestUtils.java1
-rw-r--r--src/test/resources/GetModules.json18
-rw-r--r--src/test/resources/application.yml7
18 files changed, 437 insertions, 59 deletions
diff --git a/docs/openapi/components.yml b/docs/openapi/components.yml
index f38ed64b..c67dad69 100644
--- a/docs/openapi/components.yml
+++ b/docs/openapi/components.yml
@@ -19,6 +19,30 @@ components:
items:
type: string
+ ModuleRequestParent:
+ type: object
+ properties:
+ operation:
+ type: string
+ enum: [read]
+ data:
+ type: object
+ properties:
+ modules:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ revision:
+ type: string
+ cmHandleProperties:
+ type: object
+ additionalProperties:
+ type: string
+ example: system-001
+
responses:
NotFound:
description: The specified resource was not found
@@ -65,3 +89,12 @@ components:
NoContent:
description: No Content
content: {}
+
+ parameters:
+ cmHandleInPath:
+ name: cmHandle
+ in: path
+ description: The identifier for a network function, network element, subnetwork, or any other cm object by managed Network CM Proxy
+ required: true
+ schema:
+ type: string \ No newline at end of file
diff --git a/docs/openapi/openapi.yml b/docs/openapi/openapi.yml
index 44747a9e..f261c0da 100644
--- a/docs/openapi/openapi.yml
+++ b/docs/openapi/openapi.yml
@@ -97,4 +97,29 @@ paths:
'401':
$ref: 'components.yml#/components/responses/Unauthorized'
'403':
- $ref: 'components.yml#/components/responses/Forbidden' \ No newline at end of file
+ $ref: 'components.yml#/components/responses/Forbidden'
+
+ /v1/ch/{cmHandle}/moduleResources:
+ post:
+ description: Retrieve module resources for one or more modules
+ tags:
+ - dmi-plugin
+ summary: Retrieve module resources
+ operationId: retrieveModuleResources
+ parameters:
+ - $ref: 'components.yml#/components/parameters/cmHandleInPath'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: 'components.yml#/components/schemas/ModuleRequestParent'
+ responses:
+ '200':
+ $ref: 'components.yml#/components/responses/Ok'
+ '400':
+ $ref: 'components.yml#/components/responses/BadRequest'
+ '401':
+ $ref: 'components.yml#/components/responses/Unauthorized'
+ '403':
+ $ref: 'components.yml#/components/responses/Forbidden'
diff --git a/pom.xml b/pom.xml
index fd1f1894..ac00de88 100644
--- a/pom.xml
+++ b/pom.xml
@@ -124,6 +124,10 @@
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
+ <dependency>
+ <groupId>net.minidev</groupId>
+ <artifactId>json-smart</artifactId>
+ </dependency>
</dependencies>
<build>
<resources>
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java b/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java
index a6ec6dfa..49db7d8b 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java
@@ -46,7 +46,7 @@ public class DmiExceptionHandler {
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
}
- @ExceptionHandler({ModulesNotFoundException.class})
+ @ExceptionHandler({ModulesNotFoundException.class, ModuleResourceNotFoundException.class})
public static ResponseEntity<Object> handleNotFoundExceptions(final DmiException exception) {
return buildErrorResponse(HttpStatus.NOT_FOUND, exception);
}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java b/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java
new file mode 100644
index 00000000..65db2712
--- /dev/null
+++ b/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java
@@ -0,0 +1,38 @@
+/*
+ * ============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.dmi.exception;
+
+public class ModuleResourceNotFoundException extends DmiException {
+
+ private static final long serialVersionUID = 4764849097602543408L;
+
+ private static final String ERROR_MESSAGE = "Module resource not found for given cmHandle: ";
+
+ /**
+ * Constructor.
+ *
+ * @param cmHandle the cm handle
+ * @param details the details of the error
+ */
+ public ModuleResourceNotFoundException(final String cmHandle, final String details) {
+ super(ERROR_MESSAGE + cmHandle, details);
+ }
+}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleReference.java b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleReference.java
new file mode 100644
index 00000000..cb9b7cbb
--- /dev/null
+++ b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleReference.java
@@ -0,0 +1,37 @@
+/*
+ * ============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.dmi.model;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Module Reference.
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ModuleReference {
+
+ private String name;
+ private String revision;
+}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java b/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
index 0e1d3d67..5725f094 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
@@ -20,14 +20,17 @@
package org.onap.cps.ncmp.dmi.rest.controller;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.dmi.model.CmHandles;
+import org.onap.cps.ncmp.dmi.model.ModuleReference;
+import org.onap.cps.ncmp.dmi.model.ModuleRequestParent;
import org.onap.cps.ncmp.dmi.rest.api.DmiPluginApi;
import org.onap.cps.ncmp.dmi.rest.api.DmiPluginInternalApi;
import org.onap.cps.ncmp.dmi.service.DmiService;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -40,9 +43,11 @@ public class DmiRestController implements DmiPluginApi, DmiPluginInternalApi {
private DmiService dmiService;
- @Autowired
- public DmiRestController(final DmiService dmiService) {
+ private ObjectMapper objectMapper;
+
+ public DmiRestController(final DmiService dmiService, final ObjectMapper objectMapper) {
this.dmiService = dmiService;
+ this.objectMapper = objectMapper;
}
@Override
@@ -52,11 +57,25 @@ public class DmiRestController implements DmiPluginApi, DmiPluginInternalApi {
return new ResponseEntity<>(modulesListAsJson, HttpStatus.OK);
}
+ @Override
+ public ResponseEntity<Object> retrieveModuleResources(@Valid final ModuleRequestParent moduleRequestParent,
+ final String cmHandle) {
+ if (moduleRequestParent.getOperation().toString().equals("read")) {
+ final var moduleReferenceList = convertRestObjectToJavaApiObject(moduleRequestParent);
+ final var response = dmiService.getModuleResources(cmHandle, moduleReferenceList);
+ if (response.isEmpty()) {
+ return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
+ }
+ return new ResponseEntity<>(response, HttpStatus.OK);
+ }
+ return new ResponseEntity<>("Unsupported operation", HttpStatus.CONFLICT);
+ }
+
/**
* This method register given list of cm-handles to ncmp.
*
* @param cmHandles list of cm-handles
- * @return (@code ResponseEntity) response entity
+ * @return (@ code ResponseEntity) response entity
*/
public ResponseEntity<String> registerCmHandles(final @Valid CmHandles cmHandles) {
final List<String> cmHandlesList = cmHandles.getCmHandles();
@@ -66,4 +85,9 @@ public class DmiRestController implements DmiPluginApi, DmiPluginInternalApi {
dmiService.registerCmHandles(cmHandlesList);
return new ResponseEntity<>("cm-handle registered successfully.", HttpStatus.CREATED);
}
+
+ private List<ModuleReference> convertRestObjectToJavaApiObject(final ModuleRequestParent moduleRequestParent) {
+ return objectMapper
+ .convertValue(moduleRequestParent.getData().getModules(), new TypeReference<List<ModuleReference>>() {});
+ }
}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
index d9196ecb..aeff3dc6 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
@@ -22,6 +22,7 @@ package org.onap.cps.ncmp.dmi.service;
import java.util.List;
import org.onap.cps.ncmp.dmi.exception.DmiException;
+import org.onap.cps.ncmp.dmi.model.ModuleReference;
/**
* Interface for handling Dmi plugin Data.
@@ -45,4 +46,12 @@ public interface DmiService {
*/
void registerCmHandles(List<String> cmHandles);
+ /**
+ * Get module resources for the given cm handle and modules.
+ *
+ * @param cmHandle cmHandle
+ * @param modules a list of module data
+ * @return returns all module resources
+ */
+ String getModuleResources(String cmHandle, List<ModuleReference> modules);
}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
index 990a421e..bf0689ca 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
@@ -23,15 +23,19 @@ package org.onap.cps.ncmp.dmi.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
+import net.minidev.json.JSONArray;
import org.apache.groovy.parser.antlr4.util.StringUtils;
import org.onap.cps.ncmp.dmi.config.DmiPluginConfig.DmiPluginProperties;
import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException;
import org.onap.cps.ncmp.dmi.exception.DmiException;
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException;
import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException;
import org.onap.cps.ncmp.dmi.model.CmHandleOperation;
import org.onap.cps.ncmp.dmi.model.CreatedCmHandle;
+import org.onap.cps.ncmp.dmi.model.ModuleReference;
import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient;
import org.onap.cps.ncmp.dmi.service.operation.SdncOperations;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,7 +43,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
-
@Service
@Slf4j
public class DmiServiceImpl implements DmiService {
@@ -53,19 +56,19 @@ public class DmiServiceImpl implements DmiService {
* Constructor.
*
* @param dmiPluginProperties dmiPluginProperties
- * @param ncmpRestClient ncmpRestClient
- * @param objectMapper objectMapper
- * @param sdncOperations sdncOperations
+ * @param ncmpRestClient ncmpRestClient
+ * @param sdncOperations sdncOperations
+ * @param objectMapper objectMapper
*/
@Autowired
public DmiServiceImpl(final DmiPluginProperties dmiPluginProperties,
- final NcmpRestClient ncmpRestClient,
- final ObjectMapper objectMapper,
- final SdncOperations sdncOperations) {
+ final NcmpRestClient ncmpRestClient,
+ final SdncOperations sdncOperations, final ObjectMapper objectMapper) {
this.dmiPluginProperties = dmiPluginProperties;
this.ncmpRestClient = ncmpRestClient;
this.objectMapper = objectMapper;
this.sdncOperations = sdncOperations;
+ this.objectMapper = objectMapper;
}
@Override
@@ -79,16 +82,33 @@ public class DmiServiceImpl implements DmiService {
return responseBody;
} else {
throw new DmiException("SDNC is not able to process request.",
- "response code : " + responseEntity.getStatusCode() + " message : " + responseEntity.getBody());
+ "response code : " + responseEntity.getStatusCode() + " message : " + responseEntity.getBody());
}
}
@Override
+ public String getModuleResources(final String cmHandle, final List<ModuleReference> moduleReferences) {
+ final JSONArray getModuleResponses = new JSONArray();
+ for (final var moduleReference : moduleReferences) {
+ final var moduleRequest = createModuleRequest(moduleReference);
+ final var responseEntity = sdncOperations.getModuleResource(cmHandle, moduleRequest);
+ if (responseEntity.getStatusCode() == HttpStatus.OK) {
+ getModuleResponses.add(responseEntity.getBody());
+ } else {
+ log.error("SDNC did not return a module resource for the given cmHandle {}", cmHandle);
+ throw new ModuleResourceNotFoundException(cmHandle,
+ "SDNC did not return a module resource for the given cmHandle.");
+ }
+ }
+ return getModuleResponses.toJSONString();
+ }
+
+ @Override
public void registerCmHandles(final List<String> cmHandles) {
final CmHandleOperation cmHandleOperation = new CmHandleOperation();
cmHandleOperation.setDmiPlugin(dmiPluginProperties.getDmiServiceName());
final List<CreatedCmHandle> createdCmHandleList = new ArrayList<>();
- for (final String cmHandle: cmHandles) {
+ for (final String cmHandle : cmHandles) {
final CreatedCmHandle createdCmHandle = new CreatedCmHandle();
createdCmHandle.setCmHandle(cmHandle);
createdCmHandleList.add(createdCmHandle);
@@ -100,7 +120,7 @@ public class DmiServiceImpl implements DmiService {
} catch (final JsonProcessingException e) {
log.error("Parsing error occurred while converting cm-handles to JSON {}", cmHandles);
throw new DmiException("Internal Server Error.",
- "Parsing error occurred while converting given cm-handles object list to JSON ");
+ "Parsing error occurred while converting given cm-handles object list to JSON ");
}
final ResponseEntity<String> responseEntity = ncmpRestClient.registerCmHandlesWithNcmp(cmHandlesJson);
if (!(responseEntity.getStatusCode() == HttpStatus.CREATED)) {
@@ -108,4 +128,20 @@ public class DmiServiceImpl implements DmiService {
}
}
+ private String createModuleRequest(final ModuleReference moduleReference) {
+ final var ietfNetconfModuleReferences = new LinkedHashMap<>();
+ ietfNetconfModuleReferences.put("ietf-netconf-monitoring:identifier", moduleReference.getName());
+ ietfNetconfModuleReferences.put("ietf-netconf-monitoring:version", moduleReference.getRevision());
+ final var writer = objectMapper.writer().withRootName("ietf-netconf-monitoring:input");
+ final String moduleRequest;
+ try {
+ moduleRequest = writer.writeValueAsString(ietfNetconfModuleReferences);
+ } catch (final JsonProcessingException e) {
+ log.error("JSON exception occurred when creating the module request for the given module reference {}",
+ moduleReference.getName());
+ throw new DmiException("Unable to process JSON.",
+ "JSON exception occurred when creating the module request.", e);
+ }
+ return moduleRequest;
+ }
}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java b/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java
index cf7c50a5..adac5e64 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java
@@ -30,6 +30,7 @@ import org.springframework.web.client.RestTemplate;
@Component
public class SdncRestconfClient {
+
private SdncProperties sdncProperties;
private RestTemplate restTemplate;
@@ -42,16 +43,34 @@ public class SdncRestconfClient {
* restconf get operation on sdnc.
*
* @param getResourceUrl sdnc get url
- *
* @return the response entity
*/
public ResponseEntity<String> getOperation(final String getResourceUrl) {
final String sdncBaseUrl = sdncProperties.getBaseUrl();
final String sdncRestconfUrl = sdncBaseUrl.concat(getResourceUrl);
+ final var httpEntity = new HttpEntity<>(configureHttpHeaders());
+ return restTemplate.getForEntity(sdncRestconfUrl, String.class, httpEntity);
+ }
+
+ /**
+ * restconf post operation on sdnc.
+ *
+ * @param postResourceUrl sdnc post resource url
+ * @param jsonData json data
+ * @return the response entity
+ */
+ public ResponseEntity<String> postOperationWithJsonData(final String postResourceUrl,
+ final String jsonData) {
+ final var sdncBaseUrl = sdncProperties.getBaseUrl();
+ final var sdncRestconfUrl = sdncBaseUrl.concat(postResourceUrl);
+ final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders());
+ return restTemplate.postForEntity(sdncRestconfUrl, httpEntity, String.class);
+ }
+
+ private HttpHeaders configureHttpHeaders() {
final var httpHeaders = new HttpHeaders();
httpHeaders.setBasicAuth(sdncProperties.getAuthUsername(), sdncProperties.getAuthPassword());
- httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString());
- final var httpEntity = new HttpEntity<>(httpHeaders);
- return restTemplate.getForEntity(sdncRestconfUrl, String.class, httpEntity);
+ httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+ return httpHeaders;
}
}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java b/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java
index 4e4e7217..0d1c3438 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java
@@ -20,7 +20,6 @@
package org.onap.cps.ncmp.dmi.service.operation;
-import org.jetbrains.annotations.NotNull;
import org.onap.cps.ncmp.dmi.config.DmiConfiguration.SdncProperties;
import org.onap.cps.ncmp.dmi.service.client.SdncRestconfClient;
import org.springframework.http.ResponseEntity;
@@ -29,29 +28,32 @@ import org.springframework.stereotype.Component;
@Component
public class SdncOperations {
- private static final String TOPOLOGY_URL_TEMPLATE = "/rests/data/network-topology:network-topology"
- + "/topology={topologyId}";
+
+ private static final String TOPOLOGY_URL_TEMPLATE_DATA =
+ "/rests/data/network-topology:network-topology/topology={topologyId}";
+ private static final String TOPOLOGY_URL_TEMPLATE_OPERATIONAL =
+ "/rests/operations/network-topology:network-topology/topology={topologyId}";
private static final String MOUNT_URL_TEMPLATE = "/node={nodeId}/yang-ext:mount";
private static final String GET_SCHEMA_URL = "/ietf-netconf-monitoring:netconf-state/schemas";
+ private static final String GET_SCHEMA_SOURCES_URL = "/ietf-netconf-monitoring:get-schema";
private SdncProperties sdncProperties;
private SdncRestconfClient sdncRestconfClient;
- private final String topologyUrl;
- private final String topologyMountUrlTemplate;
+ private final String topologyUrlData;
+ private final String topologyUrlOperational;
/**
- * Constructor for {@code SdncOperations}. This method also manipulates
- * url properties.
+ * Constructor for {@code SdncOperations}. This method also manipulates url properties.
*
- * @param sdncProperties {@code SdncProperties}
+ * @param sdncProperties {@code SdncProperties}
* @param sdncRestconfClient {@code SdncRestconfClient}
*/
-
public SdncOperations(final SdncProperties sdncProperties, final SdncRestconfClient sdncRestconfClient) {
this.sdncProperties = sdncProperties;
this.sdncRestconfClient = sdncRestconfClient;
- topologyUrl = TOPOLOGY_URL_TEMPLATE.replace("{topologyId}", this.sdncProperties.getTopologyId());
- topologyMountUrlTemplate = topologyUrl + MOUNT_URL_TEMPLATE;
+ topologyUrlOperational =
+ TOPOLOGY_URL_TEMPLATE_OPERATIONAL.replace("{topologyId}", this.sdncProperties.getTopologyId());
+ topologyUrlData = TOPOLOGY_URL_TEMPLATE_DATA.replace("{topologyId}", this.sdncProperties.getTopologyId());
}
/**
@@ -65,11 +67,28 @@ public class SdncOperations {
return sdncRestconfClient.getOperation(urlWithNodeId);
}
- @NotNull
+ /**
+ * Get module schema.
+ *
+ * @param nodeId node ID
+ * @param moduleProperties module properties
+ * @return response entity
+ */
+ public ResponseEntity<String> getModuleResource(final String nodeId, final String moduleProperties) {
+ final String getYangResourceUrl = prepareGetOperationSchemaUrl(nodeId);
+ return sdncRestconfClient.postOperationWithJsonData(getYangResourceUrl, moduleProperties);
+ }
+
private String prepareGetSchemaUrl(final String nodeId) {
- final String topologyMountUrl = topologyMountUrlTemplate;
+ final var topologyMountUrl = topologyUrlData + MOUNT_URL_TEMPLATE;
final String topologyMountUrlWithNodeId = topologyMountUrl.replace("{nodeId}", nodeId);
final String resourceUrl = topologyMountUrlWithNodeId.concat(GET_SCHEMA_URL);
return resourceUrl;
}
+
+ private String prepareGetOperationSchemaUrl(final String nodeId) {
+ final var topologyMountUrl = topologyUrlOperational + MOUNT_URL_TEMPLATE;
+ final var topologyMountUrlWithNodeId = topologyMountUrl.replace("{nodeId}", nodeId);
+ return topologyMountUrlWithNodeId.concat(GET_SCHEMA_SOURCES_URL);
+ }
}
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
index 993b80c6..03bffe4b 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
@@ -20,8 +20,11 @@
package org.onap.cps.ncmp.dmi.rest.controller
+import org.onap.cps.ncmp.dmi.TestUtils
import org.onap.cps.ncmp.dmi.exception.DmiException
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException
import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException
+import org.onap.cps.ncmp.dmi.model.ModuleReference
import org.onap.cps.ncmp.dmi.service.DmiService
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
@@ -55,7 +58,7 @@ class DmiRestControllerSpec extends Specification {
def someJson = 'some-json'
mockDmiService.getModulesForCmHandle('node1') >> someJson
when: 'post is being called'
- def response = mvc.perform( post(getModuleUrl)
+ def response = mvc.perform(post(getModuleUrl)
.contentType(MediaType.APPLICATION_JSON))
.andReturn().response
then: 'status is OK'
@@ -70,16 +73,16 @@ class DmiRestControllerSpec extends Specification {
and: 'get modules for cm-handle throws #exceptionClass'
mockDmiService.getModulesForCmHandle('node1') >> { throw Mock(exceptionClass) }
when: 'post is invoked'
- def response = mvc.perform( post(getModuleUrl)
+ def response = mvc.perform(post(getModuleUrl)
.contentType(MediaType.APPLICATION_JSON))
.andReturn().response
then: 'response status is #expectedResponse'
response.status == expectedResponse
where: 'the scenario is #scenario'
- scenario | exceptionClass || expectedResponse
- 'dmi service exception' | DmiException.class || HttpStatus.INTERNAL_SERVER_ERROR.value()
- 'no modules found' | ModulesNotFoundException.class || HttpStatus.NOT_FOUND.value()
- 'any other runtime exception' | RuntimeException.class || HttpStatus.INTERNAL_SERVER_ERROR.value()
+ scenario | exceptionClass || expectedResponse
+ 'dmi service exception' | DmiException.class || HttpStatus.INTERNAL_SERVER_ERROR.value()
+ 'no modules found' | ModulesNotFoundException.class || HttpStatus.NOT_FOUND.value()
+ 'any other runtime exception' | RuntimeException.class || HttpStatus.INTERNAL_SERVER_ERROR.value()
}
def 'Register given list of cm handles.'() {
@@ -112,4 +115,41 @@ class DmiRestControllerSpec extends Specification {
and: 'dmi service is not called'
0 * mockDmiService.registerCmHandles(_)
}
+
+ def 'Retrieve module resources.'() {
+ given: 'an endpoint and json data'
+ def getModulesEndpoint = "$basePathV1/ch/some-cm-handle/moduleResources"
+ def jsonData = TestUtils.getResourceFileContent('GetModules.json')
+ and: 'the DMI service returns some json data'
+ ModuleReference moduleReference1 = new ModuleReference()
+ moduleReference1.name = 'ietf-yang-library'
+ moduleReference1.revision = '2016-06-21'
+ ModuleReference moduleReference2 = new ModuleReference()
+ moduleReference2.name = 'nc-notifications'
+ moduleReference2.revision = '2008-07-14'
+ def moduleReferences = [moduleReference1, moduleReference2]
+ mockDmiService.getModuleResources('some-cm-handle', moduleReferences ) >> '{some-json}'
+ when: 'get module resource api is invoked'
+ def response = mvc.perform(post(getModulesEndpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonData)).andReturn().response
+ then:'a OK status is returned'
+ response.status == HttpStatus.OK.value()
+ and: 'the expected response is returned'
+ response.getContentAsString() == '{some-json}'
+ }
+
+ def 'Retrieve module resources with exception handling.'() {
+ given: 'an endpoint and json data'
+ def getModulesEndpoint = "$basePathV1/ch/some-cm-handle/moduleResources"
+ def jsonData = TestUtils.getResourceFileContent('GetModules.json')
+ and: 'the service method is invoked to get module resources and throws an exception'
+ mockDmiService.getModuleResources('some-cm-handle', _) >> { throw Mock(ModuleResourceNotFoundException.class) }
+ when: 'get module resource api is invoked'
+ def response = mvc.perform(post(getModulesEndpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonData)).andReturn().response
+ then: 'a not found status is returned'
+ response.status == HttpStatus.NOT_FOUND.value()
+ }
}
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
index 9d6bc358..1854b24d 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
@@ -25,7 +25,9 @@ import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.dmi.config.DmiPluginConfig
import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException
import org.onap.cps.ncmp.dmi.exception.DmiException
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException
import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException
+import org.onap.cps.ncmp.dmi.model.ModuleReference
import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient
import org.onap.cps.ncmp.dmi.service.operation.SdncOperations
import org.springframework.http.HttpStatus
@@ -40,7 +42,7 @@ class DmiServiceImplSpec extends Specification {
def objectMapper = new ObjectMapper()
def mockObjectMapper = Mock(ObjectMapper)
def mockSdncOperations = Mock(SdncOperations)
- def objectUnderTest = new DmiServiceImpl(mockDmiPluginProperties, mockNcmpRestClient, objectMapper, mockSdncOperations)
+ def objectUnderTest = new DmiServiceImpl(mockDmiPluginProperties, mockNcmpRestClient, mockSdncOperations, objectMapper)
def 'Call get modules for cm-handle on dmi Service.'() {
given: 'cm handle id'
@@ -62,7 +64,7 @@ class DmiServiceImplSpec extends Specification {
when: 'get modules for cm-handle is called'
objectUnderTest.getModulesForCmHandle(cmHandle)
then: 'dmi exception is thrown'
- thrown( DmiException )
+ thrown(DmiException)
}
def 'Call get modules for cm-handle and SDNC returns OK with empty body.'() {
@@ -73,7 +75,7 @@ class DmiServiceImplSpec extends Specification {
when: 'get modules for cm-handle is called'
objectUnderTest.getModulesForCmHandle(cmHandle)
then: 'ModulesNotFoundException is thrown'
- thrown( ModulesNotFoundException )
+ thrown(ModulesNotFoundException)
}
def 'Register cm handles with ncmp.'() {
@@ -100,9 +102,9 @@ class DmiServiceImplSpec extends Specification {
then: 'a registration exception is thrown'
thrown(CmHandleRegistrationException.class)
where: 'given #scenario'
- scenario | responseEntity
- 'ncmp rest client returns bad request' | new ResponseEntity<>(HttpStatus.BAD_REQUEST)
- 'ncmp rest client returns internal server error'| new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)
+ scenario | responseEntity
+ 'ncmp rest client returns bad request' | new ResponseEntity<>(HttpStatus.BAD_REQUEST)
+ 'ncmp rest client returns internal server error' | new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)
}
def 'Register cm handles with ncmp with wrong data.'() {
@@ -116,4 +118,34 @@ class DmiServiceImplSpec extends Specification {
then: 'a dmi exception is thrown'
thrown(DmiException.class)
}
+
+ def 'Get module resources.'() {
+ given: 'cmHandle, expected jsons and module reference list'
+ def cmHandle = 'some-cmHandle'
+ def excpectedModulesJson1 = '{"ietf-netconf-monitoring:input":{"ietf-netconf-monitoring:identifier":"mRef1","ietf-netconf-monitoring:version":"mRefV1"}}'
+ def excpectedModulesJson2 = '{"ietf-netconf-monitoring:input":{"ietf-netconf-monitoring:identifier":"mRef2","ietf-netconf-monitoring:version":"mRefV2"}}'
+ def mRef1 = Mock(ModuleReference.class)
+ def mRef2 = Mock(ModuleReference.class)
+ mRef1.getName() >> 'mRef1'
+ mRef1.getRevision() >> 'mRefV1'
+ mRef2.getName() >> 'mRef2'
+ mRef2.getRevision() >> 'mRefV2'
+ def moduleList = [mRef1, mRef2] as LinkedList<ModuleReference>
+ when: 'get module resources is invoked with the given cm handle and a module list'
+ def response = objectUnderTest.getModuleResources(cmHandle, moduleList)
+ then: 'then get modules resources called correctly'
+ 1 * mockSdncOperations.getModuleResource(cmHandle,excpectedModulesJson1) >> new ResponseEntity<String>('response-body1', HttpStatus.OK)
+ 1 * mockSdncOperations.getModuleResource(cmHandle,excpectedModulesJson2) >> new ResponseEntity<String>('response-body2', HttpStatus.OK)
+ then: 'the response contains the expected response body'
+ response.contains('["response-body1","response-body2"]')
+ }
+
+ def 'Get module resources for a failed get module schema request.'() {
+ given: 'get module schema is invoked and returns not found'
+ mockSdncOperations.getModuleResource(_ as String, _ as String) >> new ResponseEntity<String>('some-response-body', HttpStatus.BAD_REQUEST)
+ when: 'get module resources is invoked with the given cm handle and a module list'
+ objectUnderTest.getModuleResources('some-cmHandle', [new ModuleReference()] as LinkedList<ModuleReference> )
+ then: 'ModuleResourceNotFoundException is thrown'
+ thrown(ModuleResourceNotFoundException)
+ }
}
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClientSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClientSpec.groovy
index 0b192f05..6d9445c1 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClientSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClientSpec.groovy
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.dmi.service.client
import org.onap.cps.ncmp.dmi.config.DmiConfiguration
import org.springframework.http.HttpEntity
-import org.springframework.http.HttpMethod
import org.springframework.http.ResponseEntity
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
@@ -37,16 +36,38 @@ class SdncRestconfClientSpec extends Specification {
given: 'a get url'
def getResourceUrl = '/getResourceUrl'
and: 'sdnc properties'
- mockSdncProperties.baseUrl >> 'http://test-sdnc-uri'
- mockSdncProperties.authUsername >> 'test-username'
- mockSdncProperties.authPassword >> 'test-password'
- mockSdncProperties.topologyId >> 'testTopologyId'
+ setupTestConfigurationData()
and: 'the rest template returns a valid response entity'
def mockResponseEntity = Mock(ResponseEntity)
- mockRestTemplate.getForEntity({ it.toString() == 'http://test-sdnc-uri/getResourceUrl' }, String.class, _ as HttpEntity) >> mockResponseEntity
+ mockRestTemplate.getForEntity({ it.toString() == 'http://some-uri/getResourceUrl' }, String.class, _ as HttpEntity) >> mockResponseEntity
when: 'GET operation is invoked'
def result = objectUnderTest.getOperation(getResourceUrl)
then: 'the output of the method is equal to the output from the test template'
result == mockResponseEntity
}
+
+ def 'SDNC POST operation called.'() {
+ given: 'json data'
+ def jsonData = 'some-json'
+ and: 'a url for get module resources'
+ def getModuleResourceUrl = '/getModuleResourceUrl'
+ and: 'configuration data'
+ setupTestConfigurationData()
+ and: 'the rest template returns a valid response entity'
+ def mockResponseEntity = Mock(ResponseEntity)
+ when: 'get module resources is invoked'
+ def result = objectUnderTest.postOperationWithJsonData(getModuleResourceUrl, jsonData)
+ then: 'the rest template is called with the correct uri and json in the body'
+ 1 * mockRestTemplate.postForEntity({ it.toString() == 'http://some-uri/getModuleResourceUrl' },
+ { it.body.contains(jsonData) }, String.class) >> mockResponseEntity
+ and: 'the output of the method is the same as the output from the test template'
+ result == mockResponseEntity
+ }
+
+ def setupTestConfigurationData() {
+ mockSdncProperties.baseUrl >> 'http://some-uri'
+ mockSdncProperties.authUsername >> 'some-username'
+ mockSdncProperties.authPassword >> 'some-password'
+ mockSdncProperties.topologyId >> 'some-topology-id'
+ }
} \ No newline at end of file
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy
index 956834ad..9b07d68e 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy
@@ -22,23 +22,38 @@ package org.onap.cps.ncmp.dmi.service.operation
import org.onap.cps.ncmp.dmi.config.DmiConfiguration
import org.onap.cps.ncmp.dmi.service.client.SdncRestconfClient
-import org.springframework.http.HttpStatus
-import org.springframework.http.ResponseEntity
+import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
-class SdncOperationsSpec extends Specification {
- def mockSdncProperties = Mock(DmiConfiguration.SdncProperties)
- def mockSdncRestClient = Mock(SdncRestconfClient)
+@SpringBootTest
+@ContextConfiguration(classes = [DmiConfiguration.SdncProperties, SdncOperations])
+class SdncOperationsSpec extends Specification {
+
+ @SpringBean
+ SdncRestconfClient mockSdncRestClient = Mock()
+ @Autowired
+ SdncOperations objectUnderTest
def 'call get modules from node to SDNC.'() {
- given: 'nodeid, topology-id, responseentity'
+ given: 'node id and url'
def nodeId = 'node1'
def expectedUrl = '/rests/data/network-topology:network-topology/topology=test-topology/node=node1/yang-ext:mount/ietf-netconf-monitoring:netconf-state/schemas'
- mockSdncProperties.getTopologyId() >> 'test-topology'
- def objectUnderTest = new SdncOperations(mockSdncProperties, mockSdncRestClient)
when: 'called get modules from node'
objectUnderTest.getModulesFromNode(nodeId)
then: 'the get operation is executed with the correct URL'
1 * mockSdncRestClient.getOperation(expectedUrl)
}
+
+ def 'Get module resources from SDNC.'() {
+ given: 'node id and url'
+ def nodeId = 'some-node'
+ def expectedUrl = '/rests/operations/network-topology:network-topology/topology=test-topology/node=some-node/yang-ext:mount/ietf-netconf-monitoring:get-schema'
+ when: 'get module resources is called with the expected parameters'
+ objectUnderTest.getModuleResource(nodeId, 'some-json-data')
+ then: 'the SDNC Rest client is invoked with the correct URL and json data'
+ 1 * mockSdncRestClient.postOperationWithJsonData(expectedUrl, 'some-json-data')
+ }
}
diff --git a/src/test/java/org/onap/cps/ncmp/dmi/TestUtils.java b/src/test/java/org/onap/cps/ncmp/dmi/TestUtils.java
index b82a6f5c..c10d91a5 100644
--- a/src/test/java/org/onap/cps/ncmp/dmi/TestUtils.java
+++ b/src/test/java/org/onap/cps/ncmp/dmi/TestUtils.java
@@ -7,6 +7,7 @@
* 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.
diff --git a/src/test/resources/GetModules.json b/src/test/resources/GetModules.json
new file mode 100644
index 00000000..23fe77c2
--- /dev/null
+++ b/src/test/resources/GetModules.json
@@ -0,0 +1,18 @@
+{
+ "operation": "read",
+ "data": {
+ "modules": [
+ {
+ "name": "ietf-yang-library",
+ "revision": "2016-06-21"
+ },
+ {
+ "name": "nc-notifications",
+ "revision": "2008-07-14"
+ }
+ ]
+ },
+ "cmHandleProperties": {
+ "subsystemId": "system-001"
+ }
+} \ No newline at end of file
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index 8ef864bd..b7c5bab9 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -24,3 +24,10 @@ security:
auth:
username: cpsuser
password: cpsr0cks!
+
+sdnc:
+ baseUrl: http://test
+ topologyId: test-topology
+ auth:
+ username: test
+ password: test