From 83e318f8c7d3cacb24b2ef9b76162b0af12e817c Mon Sep 17 00:00:00 2001 From: Hanif Kukkalli Date: Tue, 23 Feb 2021 12:26:39 +0100 Subject: Implement getDataNode(anchorName, xPath) in NF-Proxy Added spring-boot-starter-validation instead of cps-ri. Added test 'Data node without leaves and without children.' to MultipartFileUtilSpec groovy test Created a method getNodeByCmHandleIdAndXpath(cmHandleId, cpsPath) Added hardcoded value of NFS DataspaceName as: "NFP-Operation". Issue-ID: CPS-172 Signed-off-by: Hanif Kukkalli Change-Id: Ie8086ede01a9c5069b1ee75864b04a0ee5e8a4f6 --- cps-nf-proxy-rest/docs/openapi/components.yaml | 30 ++++++++++++++- cps-nf-proxy-rest/docs/openapi/openapi.yml | 6 +-- cps-nf-proxy-rest/docs/openapi/xnfProxy.yml | 31 ++++++--------- .../nfproxy/rest/controller/NfProxyController.java | 29 ++++++++++---- .../rest/controller/NfProxyControllerSpec.groovy | 43 ++++++++++++++------- cps-nf-proxy-service/pom.xml | 6 +-- .../onap/cps/nfproxy/api/NfProxyDataService.java | 44 ++++++++++++++++++++++ .../nfproxy/api/impl/NfProxyDataServiceImpl.java | 43 +++++++++++++++++++++ .../cps/rest/utils/MultipartFileUtilSpec.groovy | 11 ++++++ 9 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java create mode 100755 cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java diff --git a/cps-nf-proxy-rest/docs/openapi/components.yaml b/cps-nf-proxy-rest/docs/openapi/components.yaml index 535219983..0b5d52a3a 100644 --- a/cps-nf-proxy-rest/docs/openapi/components.yaml +++ b/cps-nf-proxy-rest/docs/openapi/components.yaml @@ -20,7 +20,29 @@ components: format: binary parameters: - + cmHandleInPath: + name: cm-handle + in: path + description: The identifier for a network function, network element, subnetwork or any other cm object by managed NF-Proxy + required: true + schema: + type: string + xpathInQuery: + name: xpath + in: query + description: xpath + required: false + schema: + type: string + default: / + includeDescendantsOptionInQuery: + name: include-descendants + in: query + description: include-descendants + required: false + schema: + type: boolean + default: false responses: NotFound: @@ -53,6 +75,12 @@ components: application/json: schema: $ref: '#/components/schemas/ErrorMessage' + NotImplemented: + description: The given path has not been implemented + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' Ok: description: OK content: diff --git a/cps-nf-proxy-rest/docs/openapi/openapi.yml b/cps-nf-proxy-rest/docs/openapi/openapi.yml index 0dbab34ee..efa8e7253 100755 --- a/cps-nf-proxy-rest/docs/openapi/openapi.yml +++ b/cps-nf-proxy-rest/docs/openapi/openapi.yml @@ -6,8 +6,6 @@ info: servers: - url: //localhost:8088/ paths: - /v1/hello-world: - $ref: 'xnfProxy.yml#/helloWorld' - /v1/hello-error: - $ref: 'xnfProxy.yml#/helloError' + /v1/cm-handles/{cm-handle}/node: + $ref: 'xnfProxy.yml#/nodeByCmHandleAndXpath' diff --git a/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml b/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml index 0bb673ac3..4abe81aff 100644 --- a/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml +++ b/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml @@ -1,9 +1,14 @@ -helloWorld: +nodeByCmHandleAndXpath: get: + description: Get a node with an option to retrieve all the children for a given cm Handle tags: - nf-proxy - summary: rest interface validation - operationId: helloWorld + summary: Get a node given a cm Handle and xpath + operationId: getNodeByCmHandleAndXpath + parameters: + - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/xpathInQuery' + - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery' responses: 200: $ref: 'components.yaml#/components/responses/Ok' @@ -13,19 +18,7 @@ helloWorld: $ref: 'components.yaml#/components/responses/Unauthorized' 403: $ref: 'components.yaml#/components/responses/Forbidden' - -helloError: - get: - tags: - - nf-proxy - summary: error handler validation - operationId: helloError - responses: - 200: - $ref: 'components.yaml#/components/responses/Ok' - 400: - $ref: 'components.yaml#/components/responses/BadRequest' - 401: - $ref: 'components.yaml#/components/responses/Unauthorized' - 403: - $ref: 'components.yaml#/components/responses/Forbidden' \ No newline at end of file + 404: + $ref: 'components.yaml#/components/responses/NotFound' + 501: + $ref: 'components.yaml#/components/responses/NotImplemented' diff --git a/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java b/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java index 99451e69a..494e7f659 100644 --- a/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java +++ b/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java @@ -2,6 +2,8 @@ * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech * ================================================================================ + * Modification Copyright (C) 2021 highstreet technologies GmbH + * ================================================================================ * 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 @@ -19,24 +21,37 @@ package org.onap.cps.nfproxy.rest.controller; +import javax.validation.Valid; +import org.onap.cps.nfproxy.api.NfProxyDataService; import org.onap.cps.nfproxy.rest.api.NfProxyApi; -import org.onap.cps.spi.exceptions.CpsException; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; +import org.onap.cps.utils.DataMapUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; + @RestController @RequestMapping("${rest.api.xnf-base-path}") public class NfProxyController implements NfProxyApi { - @Override - public ResponseEntity helloWorld() { - return new ResponseEntity<>("Hello World!", HttpStatus.OK); - } + private static final String XPATH_ROOT = "/"; + + @Autowired + private NfProxyDataService nfProxyDataService; @Override - public ResponseEntity helloError() { - throw new CpsException("Example error Message", "Example error description"); + public ResponseEntity getNodeByCmHandleAndXpath(final String cmHandle, @Valid final String xpath, + @Valid final Boolean includeDescendants) { + if (XPATH_ROOT.equals(xpath)) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants) + ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS; + final DataNode dataNode = nfProxyDataService.getDataNode(cmHandle, xpath, fetchDescendantsOption); + return new ResponseEntity<>(DataMapUtils.toDataMap(dataNode), HttpStatus.OK); } } diff --git a/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy b/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy index 874a1b004..3cd6b9a9f 100644 --- a/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy +++ b/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy @@ -2,6 +2,8 @@ * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech * ================================================================================ + * Modification Copyright (C) 2021 highstreet technologies GmbH + * ================================================================================ * 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 @@ -19,13 +21,19 @@ package org.onap.cps.nfproxy.rest.controller +import org.onap.cps.nfproxy.api.NfProxyDataService +import org.onap.cps.spi.model.DataNodeBuilder +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.WebMvcTest import org.springframework.http.HttpStatus import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders import spock.lang.Specification +import spock.lang.Unroll + +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get @WebMvcTest class NfProxyControllerSpec extends Specification { @@ -33,22 +41,31 @@ class NfProxyControllerSpec extends Specification { @Autowired MockMvc mvc + @SpringBean + NfProxyDataService mockNfProxyDataService = Mock() + @Value('${rest.api.xnf-base-path}') def basePath - def 'Hello world method invocation.'(){ - when: 'hello-world request performed' - def response = mvc.perform(MockMvcRequestBuilders.get("$basePath/v1/hello-world")).andReturn().response - then: 'success response returned' - response.status == HttpStatus.OK.value() - response.getContentAsString().contains("Hello World!") + def dataNodeBaseEndpoint + + def setup() { + dataNodeBaseEndpoint = "$basePath/v1" } - def 'Example error handling.'(){ - when: 'hello-error request performed' - def response = mvc.perform(MockMvcRequestBuilders.get("$basePath/v1/hello-error")).andReturn().response - then: 'error response returned' - response.status == HttpStatus.INTERNAL_SERVER_ERROR.value() - response.getContentAsString().contains("Example error") + @Unroll + def 'Get data node.'() { + given: 'the service returns a data node' + def xpath = 'some xpath' + def cmHandle = 'some handle' + def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(["leaf": "value"]).build() + def endpoint = "$dataNodeBaseEndpoint/cm-handles/$cmHandle/node" + mockNfProxyDataService.getDataNode(cmHandle, xpath, OMIT_DESCENDANTS) >> dataNode + when: 'get request is performed through REST API' + def response = mvc.perform(get(endpoint).param('xpath', xpath)).andReturn().response + then: 'a success response is returned' + response.status == HttpStatus.OK.value() + and: 'response contains expected leaf and value' + response.contentAsString.contains('"leaf":"value"') } } diff --git a/cps-nf-proxy-service/pom.xml b/cps-nf-proxy-service/pom.xml index 67ffaeb20..593d6cf79 100644 --- a/cps-nf-proxy-service/pom.xml +++ b/cps-nf-proxy-service/pom.xml @@ -18,12 +18,12 @@ - ${project.groupId} - cps-service + org.springframework.boot + spring-boot-starter-validation ${project.groupId} - cps-ri + cps-service \ No newline at end of file diff --git a/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java new file mode 100644 index 000000000..644dfab11 --- /dev/null +++ b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java @@ -0,0 +1,44 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * ================================================================================ + * 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.nfproxy.api; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; + +/* + * Datastore interface for handling CPS data. + */ +public interface NfProxyDataService { + + /** + * Retrieves datanode by XPath for a given cm handle. + * + * @param cmHandle The identifier for a network function, network element, subnetwork or any other + * cm object by managed NF-Proxy + * @param xpath xpath + * @param fetchDescendantsOption defines the scope of data to fetch: either single node or all the descendant nodes + * (recursively) as well + * @return data node object + */ + DataNode getDataNode(@NonNull String cmHandle, @NonNull String xpath, + @NonNull FetchDescendantsOption fetchDescendantsOption); + +} diff --git a/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java new file mode 100755 index 000000000..d30702e77 --- /dev/null +++ b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * ================================================================================ + * 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.nfproxy.api.impl; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.nfproxy.api.NfProxyDataService; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NfProxyDataServiceImpl implements NfProxyDataService { + + private static final String NF_PROXY_DATASPACE_NAME = "NFP-Operational"; + + @Autowired + private CpsDataService cpsDataService; + + @Override + public DataNode getDataNode(@NonNull final String cmHandle, @NonNull final String xpath, + @NonNull final FetchDescendantsOption fetchDescendantsOption) { + return cpsDataService.getDataNode(NF_PROXY_DATASPACE_NAME, cmHandle, xpath, fetchDescendantsOption); + } +} diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy index 3e2bdec37..2c3d46e5d 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy @@ -21,6 +21,8 @@ package org.onap.cps.rest.utils import org.onap.cps.spi.exceptions.CpsException import org.onap.cps.spi.exceptions.ModelValidationException +import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.DataMapUtils import org.springframework.mock.web.MockMultipartFile import org.springframework.web.multipart.MultipartFile import spock.lang.Specification @@ -28,6 +30,15 @@ import spock.lang.Unroll class MultipartFileUtilSpec extends Specification { + def 'Data node without leaves and without children.'() { + given: 'a datanode with no leaves and no children' + def dataNodeWithoutData = new DataNodeBuilder().withXpath('some xpath').build() + when: 'it is converted to a map' + def result = DataMapUtils.toDataMap(dataNodeWithoutData) + then: 'an empty object map is returned' + result.isEmpty() + } + def 'Extract yang resource from yang file.'() { given: 'uploaded yang file' def multipartFile = new MockMultipartFile("file", "filename.yang", "text/plain", "content".getBytes()) -- cgit 1.2.3-korg