diff options
author | Renu Kumari <renu.kumari@bell.ca> | 2021-10-22 00:50:29 -0400 |
---|---|---|
committer | Renu Kumari <renu.kumari@bell.ca> | 2021-11-02 10:02:02 -0400 |
commit | 306cadd63b69274afbb3073d35a15381a12d6389 (patch) | |
tree | 548a631648bdb0116a4765f4f244957a5a608ee2 | |
parent | b8c23f7eea015f22cef9d3638b276025f38a1ff6 (diff) |
Refactor Get Modules by CMHandle flow
- Used jayway to convert sdnc response to POJO to clean up classes
- Moved SDNC error handling to the SDNCOperation
- Added SdncException
- Added spring-security test Mock user in the controllerSpec
Issue-ID: CPS-571
Signed-off-by: Renu Kumari <renu.kumari@bell.ca>
Change-Id: Ice91e48536dd2e309721440b61d37a49ce43b403
-rw-r--r-- | pom.xml | 9 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java (renamed from src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemaList.java) | 35 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java | 116 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java (renamed from src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemaProperties.java) | 12 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemas.java | 36 | ||||
-rw-r--r-- | src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java | 53 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/model/ModuleSchemaPropertiesSpec.groovy | 41 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy | 27 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy | 88 | ||||
-rw-r--r-- | src/test/groovy/org/onap/cps/ncmp/dmi/service/operation/SdncOperationsSpec.groovy | 82 |
10 files changed, 238 insertions, 261 deletions
@@ -78,6 +78,10 @@ <artifactId>groovy</artifactId> </dependency> <dependency> + <groupId>com.jayway.jsonpath</groupId> + <artifactId>json-path</artifactId> + </dependency> + <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> @@ -99,6 +103,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.6.2</version> diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemaList.java b/src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java index a4af1761..a83485a9 100644 --- a/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemaList.java +++ b/src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,19 +18,32 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.dmi.service.model; +package org.onap.cps.ncmp.dmi.exception; -import java.util.List; -import lombok.Getter; -import lombok.Setter; +import org.springframework.http.HttpStatus; -/** - * ModuleSchemaList. +/* +Use this exception when SDNC contract fails */ -@Getter -@Setter -public class ModuleSchemaList { +public class SdncException extends DmiException { + + private static final long serialVersionUID = -2076096996672060566L; + + /** + * Constructor. + * + * @param message message + * @param httpStatus httpStatus + * @param responseBody responseBody + */ + public SdncException(final String message, final HttpStatus httpStatus, final String responseBody) { + super(message, String.format("sdnc http status: %s, response body : %s ", + httpStatus.toString(), + responseBody)); + } - private List<ModuleSchemaProperties> schema; + public SdncException(final String message, final String details, final Throwable cause) { + super(message, details, cause); + } } 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 b66e5c1b..10ae1cf8 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 @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,12 +26,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; -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; @@ -45,8 +46,7 @@ import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient; import org.onap.cps.ncmp.dmi.service.model.CmHandleOperation; import org.onap.cps.ncmp.dmi.service.model.CreatedCmHandle; import org.onap.cps.ncmp.dmi.service.model.ModuleReference; -import org.onap.cps.ncmp.dmi.service.model.ModuleSchemaProperties; -import org.onap.cps.ncmp.dmi.service.model.ModuleSchemas; +import org.onap.cps.ncmp.dmi.service.model.ModuleSchema; import org.onap.cps.ncmp.dmi.service.operation.SdncOperations; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -75,8 +75,8 @@ public class DmiServiceImpl implements DmiService { * @param objectMapper objectMapper */ public DmiServiceImpl(final DmiPluginProperties dmiPluginProperties, - final NcmpRestClient ncmpRestClient, - final SdncOperations sdncOperations, final ObjectMapper objectMapper) { + final NcmpRestClient ncmpRestClient, + final SdncOperations sdncOperations, final ObjectMapper objectMapper) { this.dmiPluginProperties = dmiPluginProperties; this.ncmpRestClient = ncmpRestClient; this.objectMapper = objectMapper; @@ -85,16 +85,14 @@ public class DmiServiceImpl implements DmiService { @Override public ModuleSet getModulesForCmHandle(final String cmHandle) throws DmiException { - final ResponseEntity<String> responseEntity = sdncOperations.getModulesFromNode(cmHandle); - if (responseEntity.getStatusCode() == HttpStatus.OK) { - final String responseBody = responseEntity.getBody(); - if (StringUtils.isEmpty(responseBody)) { - throw new ModulesNotFoundException(cmHandle, "SDNC returned no modules for given cm-handle."); - } - return createModuleSchema(responseBody); + final Collection<ModuleSchema> moduleSchemas = sdncOperations.getModuleSchemasFromNode(cmHandle); + if (moduleSchemas.isEmpty()) { + throw new ModulesNotFoundException(cmHandle, "SDNC returned no modules for given cm-handle."); } else { - throw new DmiException("SDNC is not able to process request.", - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + final ModuleSet moduleSet = new ModuleSet(); + moduleSchemas.forEach(moduleSchema -> + moduleSet.addSchemasItem(toModuleSetSchemas(moduleSchema))); + return moduleSet; } } @@ -109,11 +107,11 @@ public class DmiServiceImpl implements DmiService { } else if (responseEntity.getStatusCode() == HttpStatus.NOT_FOUND) { 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."); + "SDNC did not return a module resource for the given cmHandle."); } else { log.error("Error occurred when getting module resources from SDNC for the given cmHandle {}", cmHandle); throw new DmiException(cmHandle, - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } return yangResources; @@ -136,7 +134,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)) { @@ -144,76 +142,54 @@ public class DmiServiceImpl implements DmiService { } } - private ModuleSet createModuleSchema(final String responseBody) { - final var moduleSchemas = convertModulesToNodeSchema(responseBody); - final List<ModuleSetSchemas> moduleSetSchemas = new ArrayList<>(); - for (final ModuleSchemaProperties schemaProperties : moduleSchemas.getSchemas().getSchema()) { - moduleSetSchemas.add(convertModulesToModuleSchemas(schemaProperties)); - } - final var moduleSet = new ModuleSet(); - moduleSet.setSchemas(moduleSetSchemas); - return moduleSet; - } - - private ModuleSetSchemas convertModulesToModuleSchemas(final ModuleSchemaProperties moduleSchemaProperties) { + private ModuleSetSchemas toModuleSetSchemas(final ModuleSchema moduleSchema) { final var moduleSetSchemas = new ModuleSetSchemas(); - moduleSetSchemas.setModuleName(moduleSchemaProperties.getIdentifier()); - moduleSetSchemas.setNamespace(moduleSchemaProperties.getNamespace()); - moduleSetSchemas.setRevision(moduleSchemaProperties.getVersion()); + moduleSetSchemas.setModuleName(moduleSchema.getIdentifier()); + moduleSetSchemas.setNamespace(moduleSchema.getNamespace()); + moduleSetSchemas.setRevision(moduleSchema.getVersion()); return moduleSetSchemas; } - private ModuleSchemas convertModulesToNodeSchema(final String modulesListAsJson) { - try { - return objectMapper.readValue(modulesListAsJson, ModuleSchemas.class); - } catch (final JsonProcessingException e) { - log.error("JSON exception occurred when converting the following modules to node schema " - + "{}", modulesListAsJson); - throw new DmiException("Unable to process JSON.", - "JSON exception occurred when mapping modules.", e); - } - } - @Override public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final String acceptParamInHeader, - final String optionsParamInQuery, - final Map<String, String> cmHandlePropertyMap) { + final @NotNull String resourceIdentifier, + final String acceptParamInHeader, + final String optionsParamInQuery, + final Map<String, String> cmHandlePropertyMap) { // not using cmHandlePropertyMap of onap dmi impl , other dmi impl might use this. final ResponseEntity<String> responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle, - resourceIdentifier, - optionsParamInQuery, - acceptParamInHeader, - RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM); + resourceIdentifier, + optionsParamInQuery, + acceptParamInHeader, + RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM); return prepareAndSendResponse(responseEntity, cmHandle); } @Override public Object getResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final String acceptParamInHeader, - final String optionsParamInQuery, - final Map<String, String> cmHandlePropertyMap) { + final @NotNull String resourceIdentifier, + final String acceptParamInHeader, + final String optionsParamInQuery, + final Map<String, String> cmHandlePropertyMap) { // not using cmHandlePropertyMap of onap dmi impl , other dmi impl might use this. final ResponseEntity<String> responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle, - resourceIdentifier, - optionsParamInQuery, - acceptParamInHeader, - REST_CONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM); + resourceIdentifier, + optionsParamInQuery, + acceptParamInHeader, + REST_CONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM); return prepareAndSendResponse(responseEntity, cmHandle); } @Override public String writeResourceDataPassthroughForCmHandle(final String cmHandle, final String resourceIdentifier, - final String dataType, final String data) { + final String dataType, final String data) { final ResponseEntity<String> responseEntity = - sdncOperations.writeResourceDataPassthroughRunning(cmHandle, resourceIdentifier, dataType, data); + sdncOperations.writeResourceDataPassthroughRunning(cmHandle, resourceIdentifier, dataType, data); if (responseEntity.getStatusCode().is2xxSuccessful()) { return responseEntity.getBody(); } else { throw new DmiException(cmHandle, - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } @@ -222,7 +198,7 @@ public class DmiServiceImpl implements DmiService { return responseEntity.getBody(); } else { throw new ResourceDataNotFound(cmHandle, - RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); + RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } @@ -236,15 +212,15 @@ public class DmiServiceImpl implements DmiService { 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()); + moduleReference.getName()); throw new DmiException("Unable to process JSON.", - "JSON exception occurred when creating the module request.", e); + "JSON exception occurred when creating the module request.", e); } return moduleRequest; } private YangResource toYangResource(final ModuleReference moduleReference, - final ResponseEntity<String> response) { + final ResponseEntity<String> response) { final YangResource yangResource = new YangResource(); yangResource.setModuleName(moduleReference.getName()); yangResource.setRevision(moduleReference.getRevision()); @@ -255,13 +231,13 @@ public class DmiServiceImpl implements DmiService { private String extractYangSourceFromBody(final ResponseEntity<String> responseEntity) { final var responseBodyAsJsonObject = new Gson().fromJson(responseEntity.getBody(), JsonObject.class); if (responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) == null - || responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) - .getAsJsonPrimitive("data") == null) { + || responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) + .getAsJsonPrimitive("data") == null) { log.error("Error occurred when trying to parse the response body from sdnc {}", responseEntity.getBody()); throw new ModuleResourceNotFoundException(responseEntity.getBody(), - "Error occurred when trying to parse the response body from sdnc."); + "Error occurred when trying to parse the response body from sdnc."); } return responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT).getAsJsonPrimitive("data") - .toString(); + .toString(); } } diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemaProperties.java b/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java index 6de7d131..c77e0e41 100644 --- a/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemaProperties.java +++ b/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,15 +22,10 @@ package org.onap.cps.ncmp.dmi.service.model; import java.util.List; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; -/** - * ModuleSchemaProperties. - */ -@Getter -@Setter -public class ModuleSchemaProperties { +@Data +public class ModuleSchema { private String identifier; private String version; diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemas.java b/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemas.java deleted file mode 100644 index a7b2430d..00000000 --- a/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchemas.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * ============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.service.model; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Getter; -import lombok.Setter; - -/** - * ModuleSchemas. - */ -@Getter -@Setter -public class ModuleSchemas { - - @JsonProperty("ietf-netconf-monitoring:schemas") - private ModuleSchemaList schemas; -} 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 bdbc3b31..43e2a5e6 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 @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +21,24 @@ package org.onap.cps.ncmp.dmi.service.operation; +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.JsonPathException; +import com.jayway.jsonpath.TypeRef; +import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.apache.groovy.parser.antlr4.util.StringUtils; import org.onap.cps.ncmp.dmi.config.DmiConfiguration.SdncProperties; +import org.onap.cps.ncmp.dmi.exception.SdncException; import org.onap.cps.ncmp.dmi.service.client.SdncRestconfClient; +import org.onap.cps.ncmp.dmi.service.model.ModuleSchema; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -41,12 +53,18 @@ public class SdncOperations { 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 static final String PATH_TO_MODULE_SCHEMAS = "$.ietf-netconf-monitoring:schemas.schema"; private SdncProperties sdncProperties; private SdncRestconfClient sdncRestconfClient; private final String topologyUrlData; private final String topologyUrlOperational; + private Configuration jsonPathConfiguration = Configuration.builder() + .mappingProvider(new JacksonMappingProvider()) + .jsonProvider(new JacksonJsonProvider()) + .build(); + /** * Constructor for {@code SdncOperations}. This method also manipulates url properties. * @@ -65,11 +83,20 @@ public class SdncOperations { * This method fetches list of modules usind sdnc client. * * @param nodeId node id for node - * @return returns {@code ResponseEntity} which contains list of modules + * @return a collection of module schemas */ - public ResponseEntity<String> getModulesFromNode(final String nodeId) { + public Collection<ModuleSchema> getModuleSchemasFromNode(final String nodeId) { final String urlWithNodeId = prepareGetSchemaUrl(nodeId); - return sdncRestconfClient.getOperation(urlWithNodeId); + final ResponseEntity<String> modulesResponseEntity = sdncRestconfClient.getOperation(urlWithNodeId); + if (modulesResponseEntity.getStatusCode() == HttpStatus.OK) { + final String modulesResponseBody = modulesResponseEntity.getBody(); + return (StringUtils.isEmpty(modulesResponseBody)) ? Collections.emptyList() + : convertToModuleSchemas(modulesResponseBody); + } else { + throw new SdncException( + String.format("SDNC failed to get Modules Schema for node %s", nodeId), + modulesResponseEntity.getStatusCode(), modulesResponseEntity.getBody()); + } } /** @@ -90,10 +117,10 @@ public class SdncOperations { /** * This method fetches the resource data for given node identifier on given resource using sdnc client. * - * @param nodeId network resource identifier - * @param resourceId resource identifier - * @param optionsParamInQuery fields query - * @param acceptParamInHeader accept parameter + * @param nodeId network resource identifier + * @param resourceId resource identifier + * @param optionsParamInQuery fields query + * @param acceptParamInHeader accept parameter * @param restconfContentQueryParam restconf content query param * @return {@code ResponseEntity} response entity */ @@ -194,4 +221,16 @@ public class SdncOperations { final String topologyMountUrl = topologyUrlData + MOUNT_URL_TEMPLATE; return topologyMountUrl.replace("{nodeId}", nodeId); } + + private List<ModuleSchema> convertToModuleSchemas(final String modulesListAsJson) { + try { + return JsonPath.using(jsonPathConfiguration).parse(modulesListAsJson).read( + PATH_TO_MODULE_SCHEMAS, new TypeRef<>() { + }); + } catch (final JsonPathException jsonPathException) { + throw new SdncException("SDNC Response processing failed", + "SDNC response is not in the expected format.", jsonPathException); + } + } + }
\ No newline at end of file diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/model/ModuleSchemaPropertiesSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/model/ModuleSchemaPropertiesSpec.groovy deleted file mode 100644 index 51dddc7d..00000000 --- a/src/test/groovy/org/onap/cps/ncmp/dmi/model/ModuleSchemaPropertiesSpec.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * ============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 org.onap.cps.ncmp.dmi.service.model.ModuleSchemaProperties -import spock.lang.Specification - -class ModuleSchemaPropertiesSpec extends Specification { - def objectUnderTest = new ModuleSchemaProperties(identifier:'some id', - version:'some version', - format:'some format', - namespace:'some namespace', - location: ['some','locations']) - - def 'Reading all properties.'() { - expect: 'all properties return the expected values' - objectUnderTest.identifier == 'some id' - objectUnderTest.version == 'some version' - objectUnderTest.format == 'some format' - objectUnderTest.namespace == 'some namespace' - objectUnderTest.location == ['some','locations'] - } -} 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 ac78928d..1446a5aa 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 @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +27,6 @@ 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.service.model.ModuleReference -import org.onap.cps.ncmp.dmi.service.model.ModuleSchemaList import org.onap.cps.ncmp.dmi.model.ModuleSet import org.onap.cps.ncmp.dmi.model.ModuleSetSchemas import org.onap.cps.ncmp.dmi.model.YangResource @@ -35,26 +35,25 @@ import org.onap.cps.ncmp.dmi.service.DmiService import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.context.annotation.Import import org.springframework.http.HttpStatus import org.springframework.http.MediaType +import org.springframework.security.test.context.support.WithMockUser import org.springframework.test.web.servlet.MockMvc import spock.lang.Specification import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put -@WebMvcTest -@AutoConfigureMockMvc(addFilters = false) +@WebMvcTest(DmiRestController) +@WithMockUser +@Import(ObjectMapper) class DmiRestControllerSpec extends Specification { @SpringBean DmiService mockDmiService = Mock() - @SpringBean - ObjectMapper mockObjectMapper = Spy() - @Autowired private MockMvc mvc @@ -83,20 +82,6 @@ class DmiRestControllerSpec extends Specification { response.getContentAsString() == '{"schemas":[{"moduleName":"some-moduleName","revision":"some-revision","namespace":"some-namespace"}]}' } - def 'Get all modules for given cm handle with invalid json.'() { - given: 'REST endpoint for getting all modules' - def getModuleUrl = "$basePathV1/ch/node1/modules" - and: 'get modules for cmHandle throws an exception' - mockObjectMapper.readValue(_ as String, _ as ModuleSchemaList) >> { throw Mock(DmiException.class) } - mockDmiService.getModulesForCmHandle('node1') >> 'some-value' - when: 'post is being called' - def response = mvc.perform(post(getModuleUrl) - .contentType(MediaType.APPLICATION_JSON)) - .andReturn().response - then: 'the status is as expected' - response.status == HttpStatus.INTERNAL_SERVER_ERROR.value() - } - def 'Get all modules for given cm handle with exception handling of #scenario.'() { given: 'REST endpoint for getting all modules' def getModuleUrl = "$basePathV1/ch/node1/modules" 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 9325d59b..d5353754 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 @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +24,6 @@ package org.onap.cps.ncmp.dmi.service import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectWriter -import org.onap.cps.ncmp.dmi.TestUtils import org.onap.cps.ncmp.dmi.config.DmiPluginConfig import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException import org.onap.cps.ncmp.dmi.exception.DmiException @@ -34,6 +34,7 @@ import org.onap.cps.ncmp.dmi.service.model.ModuleReference import org.onap.cps.ncmp.dmi.model.YangResource import org.onap.cps.ncmp.dmi.model.YangResources import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient +import org.onap.cps.ncmp.dmi.service.model.ModuleSchema import org.onap.cps.ncmp.dmi.service.operation.SdncOperations import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -49,50 +50,35 @@ class DmiServiceImplSpec extends Specification { def mockSdncOperations = Mock(SdncOperations) def objectUnderTest = new DmiServiceImpl(mockDmiPluginProperties, mockNcmpRestClient, mockSdncOperations, spyObjectMapper) - def 'Call get modules for cm-handle on dmi Service.'() { - given: 'cm handle id' - def cmHandle = 'node1' - and: 'request operation returns OK' - def body = TestUtils.getResourceFileContent('ModuleSchema.json') - mockSdncOperations.getModulesFromNode(cmHandle) >> new ResponseEntity<String>(body, HttpStatus.OK) - when: 'get modules for cm-handle is called' - def result = objectUnderTest.getModulesForCmHandle(cmHandle) - then: 'result is equal to the response from the SDNC service' - result.toString().contains('moduleName: example-identifier') - result.toString().contains('revision: example-version') - } - - def 'Call get modules for cm-handle with invalid json.'() { - given: 'cm handle id' + def ' Get modules for a cm-handle.'() { + given: 'a cm handle' def cmHandle = 'node1' - and: 'request operation returns invalid json' - def body = TestUtils.getResourceFileContent('ModuleSchema.json') - mockSdncOperations.getModulesFromNode(cmHandle) >> new ResponseEntity<String>('invalid json', HttpStatus.OK) + and: 'sdnc operations returns one module schema for the cmhandle' + def moduleSchema = new ModuleSchema( + identifier: "example-identifier", + namespace: "example:namespace", + version: "example-version") + mockSdncOperations.getModuleSchemasFromNode(cmHandle) >> List.of(moduleSchema) when: 'get modules for cm-handle is called' def result = objectUnderTest.getModulesForCmHandle(cmHandle) - then: 'a dmi exception is thrown' - thrown(DmiException) - } - - def 'Call get modules for cm-handle and SDNC returns "bad request" status.'() { - given: 'cm handle id' - def cmHandle = 'node1' - and: 'get modules from node returns "bad request" status' - mockSdncOperations.getModulesFromNode(cmHandle) >> new ResponseEntity<String>('body', HttpStatus.BAD_REQUEST) - when: 'get modules for cm-handle is called' - objectUnderTest.getModulesForCmHandle(cmHandle) - then: 'dmi exception is thrown' - thrown(DmiException) + then: 'one module is returned' + result.schemas.size() == 1 + and: 'module has expected values' + with(result.schemas[0]) { + it.getRevision() == moduleSchema.getVersion() + it.getModuleName() == moduleSchema.getIdentifier() + it.getNamespace() == moduleSchema.getNamespace(); + } } - def 'Call get modules for cm-handle and SDNC returns OK with empty body.'() { + def 'no modules found for the cmhandle.'() { given: 'cm handle id' def cmHandle = 'node1' - and: 'get modules for cm-handle returns OK with empty body' - mockSdncOperations.getModulesFromNode(cmHandle) >> new ResponseEntity<String>('', HttpStatus.OK) + and: 'sdnc operations returns no modules' + mockSdncOperations.getModuleSchemasFromNode(cmHandle) >> Collections.emptyList(); when: 'get modules for cm-handle is called' objectUnderTest.getModulesForCmHandle(cmHandle) - then: 'ModulesNotFoundException is thrown' + then: 'module not found exception is thrown' thrown(ModulesNotFoundException) } @@ -140,14 +126,14 @@ class DmiServiceImplSpec extends Specification { def 'Get multiple module resources.'() { given: 'a cmHandle and module reference list' def cmHandle = 'some-cmHandle' - def moduleReference1 = new ModuleReference(name: 'name-1',revision: 'revision-1') - def moduleReference2 = new ModuleReference(name: 'name-2',revision: 'revision-2') + def moduleReference1 = new ModuleReference(name: 'name-1', revision: 'revision-1') + def moduleReference2 = new ModuleReference(name: 'name-2', revision: 'revision-2') def moduleList = [moduleReference1, moduleReference2] when: 'get module resources is invoked with the given cm handle and a module list' def result = objectUnderTest.getModuleResources(cmHandle, moduleList) then: 'get modules resources is called twice' 2 * mockSdncOperations.getModuleResource(cmHandle, _) >>> [new ResponseEntity<String>('{"ietf-netconf-monitoring:output": {"data": "some-data1"}}', HttpStatus.OK), - new ResponseEntity<String>('{"ietf-netconf-monitoring:output": {"data": "some-data2"}}', HttpStatus.OK)] + new ResponseEntity<String>('{"ietf-netconf-monitoring:output": {"data": "some-data2"}}', HttpStatus.OK)] and: 'the result is a yang resources object with the expected names, revisions and yang-sources' def yangResources = new YangResources() def yangResource1 = new YangResource(yangSource: '"some-data1"', moduleName: 'name-1', revision: 'revision-1') @@ -160,7 +146,7 @@ class DmiServiceImplSpec extends Specification { def 'Get a module resource with module resource not found exception for #scenario.'() { given: 'a cmHandle and module reference list' def cmHandle = 'some-cmHandle' - def moduleReference = new ModuleReference(name: 'NAME',revision: 'REVISION') + def moduleReference = new ModuleReference(name: 'NAME', revision: 'REVISION') def moduleList = [moduleReference] when: 'get module resources is invoked with the given cm handle and a module list' objectUnderTest.getModuleResources(cmHandle, moduleList) @@ -216,8 +202,8 @@ class DmiServiceImplSpec extends Specification { mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, optionsParam, acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK) when: 'get resource data from cm handles service method invoked' def response = objectUnderTest.getResourceDataOperationalForCmHandle(cmHandle, - resourceId, acceptHeaderParam, - optionsParam, null) + resourceId, acceptHeaderParam, + optionsParam, null) then: 'response have expected json' response == 'response json' } @@ -232,8 +218,8 @@ class DmiServiceImplSpec extends Specification { mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, optionsParam, acceptHeaderParam, _ as String) >> new ResponseEntity<>(HttpStatus.NOT_FOUND) when: 'get resource data from cm handles service method invoked' objectUnderTest.getResourceDataOperationalForCmHandle(cmHandle, - resourceId, acceptHeaderParam, - optionsParam, null) + resourceId, acceptHeaderParam, + optionsParam, null) then: 'resource data not found' thrown(ResourceDataNotFound.class) } @@ -247,11 +233,11 @@ class DmiServiceImplSpec extends Specification { def contentQuery = 'content=config' and: 'sdnc operation returns OK response' mockSdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceId, optionsParam, - acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK) + acceptHeaderParam, contentQuery) >> new ResponseEntity<>('response json', HttpStatus.OK) when: 'get resource data from cm handles service method invoked' def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceId, acceptHeaderParam, - optionsParam, null) + resourceId, acceptHeaderParam, + optionsParam, null) then: 'response have expected json' response == 'response json' } @@ -261,7 +247,7 @@ class DmiServiceImplSpec extends Specification { mockSdncOperations.writeResourceDataPassthroughRunning(_, _, _, _) >> new ResponseEntity<String>('response json', httpResponse) when: 'write resource data for cm handle method invoked' def response = objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', - 'some-resourceIdentifier', 'some-dataType', '{some-data}') + 'some-resourceIdentifier', 'some-dataType', '{some-data}') then: 'the response contains the expected json data from sdnc' response == 'response json' where: 'the following values are used' @@ -273,10 +259,10 @@ class DmiServiceImplSpec extends Specification { def 'Write resource data using for passthrough running for the given cm handle with #scenario.'() { given: 'sdnc returns a created response' mockSdncOperations.writeResourceDataPassthroughRunning('some-cmHandle', - 'some-resourceIdentifier', 'some-dataType', requestBody) >> new ResponseEntity<String>('response json', HttpStatus.CREATED) + 'some-resourceIdentifier', 'some-dataType', requestBody) >> new ResponseEntity<String>('response json', HttpStatus.CREATED) when: 'write resource data from cm handles service method invoked' def response = objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', - 'some-resourceIdentifier', 'some-dataType', requestBody) + 'some-resourceIdentifier', 'some-dataType', requestBody) then: 'response have expected json' response == 'response json' where: 'given request body' @@ -290,7 +276,7 @@ class DmiServiceImplSpec extends Specification { mockSdncOperations.writeResourceDataPassthroughRunning(_, _, _, _) >> new ResponseEntity<String>('response json', HttpStatus.INTERNAL_SERVER_ERROR) when: 'write resource data for pass through method is invoked' objectUnderTest.writeResourceDataPassthroughForCmHandle('some-cmHandle', - 'some-resourceIdentifier', 'some-dataType', _ as String) + 'some-resourceIdentifier', 'some-dataType', _ as String) then: 'a dmi exception is thrown' thrown(DmiException.class) } 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 4411971a..2ce870ab 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 @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +21,16 @@ package org.onap.cps.ncmp.dmi.service.operation +import org.onap.cps.ncmp.dmi.TestUtils import org.onap.cps.ncmp.dmi.config.DmiConfiguration +import org.onap.cps.ncmp.dmi.exception.SdncException import org.onap.cps.ncmp.dmi.service.client.SdncRestconfClient import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @@ -39,14 +44,59 @@ class SdncOperationsSpec extends Specification { @Autowired SdncOperations objectUnderTest - def 'call get modules from node to SDNC.'() { + def 'get modules from node.'() { 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' - when: 'called get modules from node' - objectUnderTest.getModulesFromNode(nodeId) - then: 'the get operation is executed with the correct URL' - 1 * mockSdncRestClient.getOperation(expectedUrl) + and: 'sdnc returns one module in response' + mockSdncRestClient.getOperation(expectedUrl) >> + ResponseEntity.ok(TestUtils.getResourceFileContent('ModuleSchema.json')) + when: 'get modules from node is called' + def moduleSchemas = objectUnderTest.getModuleSchemasFromNode(nodeId) + then: 'one module is found' + moduleSchemas.size() == 1 + and: 'module schema has expected values' + with(moduleSchemas[0]) { + it.getIdentifier() == "example-identifier" + it.getNamespace() == "example:namespace" + it.getVersion() == "example-version" + it.getFormat() == "example-format" + it.getLocation() == ["example-location"] + } + } + + def 'No modules from Node: SDNC Response - #scenario .'() { + 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' + and: 'sdnc operation returns #scenario' + mockSdncRestClient.getOperation(expectedUrl) >> ResponseEntity.ok(responseBody) + when: 'modules from node is called' + def moduleSchemas = objectUnderTest.getModuleSchemasFromNode(nodeId) + then: 'no modules are returned' + moduleSchemas.size() == 0 + where: + scenario | responseBody + 'empty response body ' | '' + 'no module schema' | '{ "ietf-netconf-monitoring:schemas" : { "schema" : [] } } ' + } + + def 'Error handling - modules from node: #scenario'() { + 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' + and: 'sdnc operation returns configured response' + mockSdncRestClient.getOperation(expectedUrl) >> new ResponseEntity<>(sdncResponseBody, sdncHttpStatus) + when: 'modules for node are fetched' + objectUnderTest.getModuleSchemasFromNode(nodeId) + then: 'SDNCException is thrown' + def thrownException = thrown(SdncException) + thrownException.getDetails().contains(expectedExceptionDetails) + where: + scenario | sdncHttpStatus | sdncResponseBody || expectedExceptionDetails + 'failed response from SDNC' | HttpStatus.BAD_REQUEST | '{ "errorMessage" : "incorrect input"}' || '{ "errorMessage" : "incorrect input"}' + 'invalid json response' | HttpStatus.OK | 'invalid-json' || 'SDNC response is not in the expected format' + 'response in unexpected json schema' | HttpStatus.OK | '{ "format" : "incorrect" }' || 'SDNC response is not in the expected format' } def 'Get module resources from SDNC.'() { @@ -64,7 +114,7 @@ class SdncOperationsSpec extends Specification { def expectedUrl = '/rests/data/network-topology:network-topology/topology=test-topology/node=node1/yang-ext:mount/testResourceId?a=1&b=2&content=testContent' when: 'called get modules from node' objectUnderTest.getResouceDataForOperationalAndRunning('node1', 'testResourceId', - '(a=1,b=2)', 'testAcceptParam', 'content=testContent') + '(a=1,b=2)', 'testAcceptParam', 'content=testContent') then: 'the get operation is executed with the correct URL' 1 * mockSdncRestClient.getOperation(expectedUrl, _ as HttpHeaders) } @@ -73,30 +123,30 @@ class SdncOperationsSpec extends Specification { given: 'expected url, topology-id, sdncOperation object' def expectedUrl = '/rests/data/network-topology:network-topology/topology=test-topology/node=node1/yang-ext:mount/testResourceId' when: 'write resource data for pass through running is called' - objectUnderTest.writeResourceDataPassthroughRunning('node1','testResourceId','application/json','requestData') + objectUnderTest.writeResourceDataPassthroughRunning('node1', 'testResourceId', 'application/json', 'requestData') then: 'the post operation is executed with the correct URL and data' 1 * mockSdncRestClient.postOperationWithJsonData(expectedUrl, 'requestData', _ as HttpHeaders) } def 'build query param list for SDNC where options contains a #scenario'() { when: 'build query param list is called with #scenario' - def result = objectUnderTest.buildQueryParamList(optionsParamInQuery,'d=4') + def result = objectUnderTest.buildQueryParamList(optionsParamInQuery, 'd=4') then: 'result equals to expected result' result == expectedResult where: 'following parameters are used' - scenario | optionsParamInQuery || expectedResult - 'single key-value pair' | '(a=x)' || ['a=x','d=4'] - 'multiple key-value pairs'| '(a=x,b=y,c=z)' || ['a=x','b=y','c=z','d=4'] - '/ as special char' | '(a=x,b=y,c=t/z)' || ['a=x','b=y','c=t/z','d=4'] - '" as special char' | '(a=x,b=y,c="z")' || ['a=x','b=y','c="z"','d=4'] - '[] as special char' | '(a=x,b=y,c=[z])' || ['a=x','b=y','c=[z]','d=4'] - '= in value' | '(a=(x=y),b=x=y)' || ['a=(x=y)','b=x=y','d=4'] + scenario | optionsParamInQuery || expectedResult + 'single key-value pair' | '(a=x)' || ['a=x', 'd=4'] + 'multiple key-value pairs' | '(a=x,b=y,c=z)' || ['a=x', 'b=y', 'c=z', 'd=4'] + '/ as special char' | '(a=x,b=y,c=t/z)' || ['a=x', 'b=y', 'c=t/z', 'd=4'] + '" as special char' | '(a=x,b=y,c="z")' || ['a=x', 'b=y', 'c="z"', 'd=4'] + '[] as special char' | '(a=x,b=y,c=[z])' || ['a=x', 'b=y', 'c=[z]', 'd=4'] + '= in value' | '(a=(x=y),b=x=y)' || ['a=(x=y)', 'b=x=y', 'd=4'] } def 'options parameters contains a comma #scenario'() { // https://jira.onap.org/browse/CPS-719 when: 'build query param list is called with #scenario' - def result = objectUnderTest.buildQueryParamList(optionsParamInQuery,'d=4') + def result = objectUnderTest.buildQueryParamList(optionsParamInQuery, 'd=4') then: 'expect 2 elements from options +1 from content query param (2+1) = 3 elements' def expectedNoOfElements = 3 and: 'results contains more elements than expected' |