summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuslan Kashapov <ruslan.kashapov@pantheon.tech>2021-04-19 12:40:01 +0300
committerRuslan Kashapov <ruslan.kashapov@pantheon.tech>2021-04-20 12:58:55 +0300
commit24bf350947acb7fcb62878932d520387bc922a96 (patch)
tree447c8ad367db6769c5e6bd66f85fcf059524999b
parent3da52076907385c8a42b817f7aceb65e0dcb7cdd (diff)
Create child data node (part 1): CPS service + REST
Issue-ID: CPS-337 Change-Id: I9c5c62d144b5301ac80e2b82a5cc66a980dad011 Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
-rwxr-xr-xcps-rest/docs/api/swagger/cpsData.yml1
-rwxr-xr-xcps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java20
-rwxr-xr-xcps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy33
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/CpsDataService.java16
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java7
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy20
6 files changed, 88 insertions, 9 deletions
diff --git a/cps-rest/docs/api/swagger/cpsData.yml b/cps-rest/docs/api/swagger/cpsData.yml
index 24644899a4..54c89661b9 100755
--- a/cps-rest/docs/api/swagger/cpsData.yml
+++ b/cps-rest/docs/api/swagger/cpsData.yml
@@ -33,6 +33,7 @@ nodesByDataspaceAndAnchor:
parameters:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
+ - $ref: 'components.yml#/components/parameters/xpathInQuery'
requestBody:
required: true
content:
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
index f466ebcef0..3385f35fe8 100755
--- a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
@@ -21,8 +21,6 @@
package org.onap.cps.rest.controller;
-import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.rest.api.CpsDataApi;
import org.onap.cps.spi.FetchDescendantsOption;
@@ -38,13 +36,19 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("${rest.api.cps-base-path}")
public class DataRestController implements CpsDataApi {
+ private static final String ROOT_XPATH = "/";
+
@Autowired
private CpsDataService cpsDataService;
@Override
- public ResponseEntity<String> createNode(@Valid final String jsonData, @NotNull final String dataspaceName,
- @NotNull @Valid final String anchorName) {
- cpsDataService.saveData(dataspaceName, anchorName, jsonData);
+ public ResponseEntity<String> createNode(final String jsonData, final String dataspaceName, final String anchorName,
+ final String parentNodeXpath) {
+ if (isRootXpath(parentNodeXpath)) {
+ cpsDataService.saveData(dataspaceName, anchorName, jsonData);
+ } else {
+ cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, jsonData);
+ }
return new ResponseEntity<>(HttpStatus.CREATED);
}
@@ -56,7 +60,7 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<Object> getNodeByDataspaceAndAnchor(final String dataspaceName, final String anchorName,
final String xpath, final Boolean includeDescendants) {
- if ("/".equals(xpath)) {
+ if (isRootXpath(xpath)) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
@@ -79,4 +83,8 @@ public class DataRestController implements CpsDataApi {
cpsDataService.replaceNodeTree(dataspaceName, anchorName, parentNodeXpath, jsonData);
return new ResponseEntity<>(HttpStatus.OK);
}
+
+ private static boolean isRootXpath(final String xpath) {
+ return ROOT_XPATH.equals(xpath);
+ }
}
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
index ef43641ea8..713dda1403 100755
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
@@ -90,7 +90,8 @@ class DataRestControllerSpec extends Specification {
dataNodeBaseEndpoint = "$basePath/v1/dataspaces/$dataspaceName"
}
- def 'Create a node.'() {
+ @Unroll
+ def 'Create a node: #scenario.'() {
given: 'some json to create a data node'
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/nodes"
def json = 'some json (this is not validated)'
@@ -98,12 +99,38 @@ class DataRestControllerSpec extends Specification {
def response =
mvc.perform(
post(endpoint)
- .contentType(MediaType.APPLICATION_JSON).content(json))
- .andReturn().response
+ .contentType(MediaType.APPLICATION_JSON)
+ .param('xpath', parentNodeXpath)
+ .content(json)
+ ).andReturn().response
then: 'a created response is returned'
response.status == HttpStatus.CREATED.value()
then: 'the java API was called with the correct parameters'
1 * mockCpsDataService.saveData(dataspaceName, anchorName, json)
+ where: 'following xpath parameters are are used'
+ scenario | parentNodeXpath
+ 'no xpath parameter' | ''
+ 'xpath parameter point root' | '/'
+ }
+
+ def 'Create a child node'() {
+ given: 'some json to create a data node'
+ def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/nodes"
+ def json = 'some json (this is not validated)'
+ and: 'parent node xpath'
+ def parentNodeXpath = 'some xpath'
+ when: 'post is invoked with datanode endpoint and json'
+ def response =
+ mvc.perform(
+ post(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .param('xpath', parentNodeXpath)
+ .content(json)
+ ).andReturn().response
+ then: 'a created response is returned'
+ response.status == HttpStatus.CREATED.value()
+ then: 'the java API was called with the correct parameters'
+ 1 * mockCpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, json)
}
@Unroll
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
index 54d9258918..8552c6c0d8 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
@@ -22,6 +22,8 @@ package org.onap.cps.api;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.onap.cps.spi.exceptions.DataValidationException;
import org.onap.cps.spi.model.DataNode;
@@ -41,6 +43,20 @@ public interface CpsDataService {
void saveData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String jsonData);
/**
+ * Persists child data fragment under existing data node for the given anchor and dataspace.
+ *
+ * @param dataspaceName dataspace name
+ * @param anchorName anchor name
+ * @param parentNodeXpath parent node xpath
+ * @param jsonData json data
+ * @throws DataValidationException when json data is invalid
+ * @throws DataNodeNotFoundException when parent node cannot be found by parent node xpath
+ * @throws AlreadyDefinedException when child data node with same xpath already exists
+ */
+ void saveData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath,
+ @NonNull String jsonData);
+
+ /**
* Retrieves datanode by XPath for given dataspace and anchor.
*
* @param dataspaceName dataspace name
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
index 6f7d6439b6..cc290bf29d 100755
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
@@ -59,6 +59,13 @@ public class CpsDataServiceImpl implements CpsDataService {
}
@Override
+ public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ final String jsonData) {
+ final DataNode dataNode = buildDataNodeFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData);
+ cpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, parentNodeXpath, dataNode);
+ }
+
+ @Override
public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
final FetchDescendantsOption fetchDescendantsOption) {
return cpsDataPersistenceService.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption);
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
index d561475273..29a2314a33 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Nordix Foundation
+ * Modifications Copyright (C) 2021 Pantheon.tech
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,6 +70,25 @@ class CpsDataServiceImplSpec extends Specification {
{ dataNode -> dataNode.xpath == '/test-tree' })
}
+ def 'Saving child data fragment under existing node.'() {
+ given: 'that the admin service will return an anchor'
+ def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build()
+ mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
+ and: 'the schema source set cache returns a schema source set'
+ def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
+ mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
+ and: 'the schema source sets returns the test-tree schema context'
+ def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('test-tree.yang')
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
+ mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
+ when: 'save data method is invoked with test-tree json data'
+ def jsonData = '{"branch": [{"name": "New"}]}'
+ objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree',jsonData)
+ then: 'the persistence service method is invoked with correct parameters'
+ 1 * mockCpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName,'/test-tree',
+ { dataNode -> dataNode.xpath == '/test-tree/branch[@name=\'New\']' })
+ }
+
@Unroll
def 'Get data node with option #fetchDescendantsOption.'() {
def xpath = '/xpath'