aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsourabh_sourabh <sourabh.sourabh@est.tech>2022-01-05 23:50:20 +0530
committerSourabh Sourabh <sourabh.sourabh@est.tech>2022-01-07 10:29:38 +0000
commit6f494f46266e8709d6b61530aa48fe76adacf965 (patch)
tree7f4becbba1e5c649ceb2157afddddd488fde6c0b
parentb3c526ff9704bb8613dc25b2ceb43f1a20b01ed4 (diff)
CPS-Core: Unable to parse JSON input with space for POST endpoint
Issue-ID: CPS-831 Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech> Change-Id: If2f5f7034f05763990001c9dd8ccd9d8dc0099cf
-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.groovy41
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy27
-rw-r--r--csit/data/test-tree.json2
4 files changed, 59 insertions, 31 deletions
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 73c2c27c9..a55b1ba7a 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
@@ -22,15 +22,16 @@
package org.onap.cps.rest.controller;
+import com.google.gson.Gson;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import javax.validation.ValidationException;
+import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.rest.api.CpsDataApi;
import org.onap.cps.spi.FetchDescendantsOption;
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;
@@ -38,23 +39,24 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("${rest.api.cps-base-path}")
+@RequiredArgsConstructor
public class DataRestController implements CpsDataApi {
private static final String ROOT_XPATH = "/";
private static final String ISO_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
private static final DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_FORMAT);
- @Autowired
- private CpsDataService cpsDataService;
+ private final CpsDataService cpsDataService;
+ private final Gson gson;
@Override
public ResponseEntity<String> createNode(final String dataspaceName, final String anchorName,
final Object jsonData, final String parentNodeXpath, final String observedTimestamp) {
if (isRootXpath(parentNodeXpath)) {
- cpsDataService.saveData(dataspaceName, anchorName, jsonData.toString(),
+ cpsDataService.saveData(dataspaceName, anchorName, gson.toJson(jsonData),
toOffsetDateTime(observedTimestamp));
} else {
- cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, jsonData.toString(),
+ cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData),
toOffsetDateTime(observedTimestamp));
}
return new ResponseEntity<>(HttpStatus.CREATED);
@@ -71,7 +73,7 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<String> addListElements(final String parentNodeXpath,
final String dataspaceName, final String anchorName, final Object jsonData, final String observedTimestamp) {
- cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, jsonData.toString(),
+ cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData),
toOffsetDateTime(observedTimestamp));
return new ResponseEntity<>(HttpStatus.CREATED);
}
@@ -89,7 +91,7 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<Object> updateNodeLeaves(final String dataspaceName,
final String anchorName, final Object jsonData, final String parentNodeXpath, final String observedTimestamp) {
- cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, jsonData.toString(),
+ cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData),
toOffsetDateTime(observedTimestamp));
return new ResponseEntity<>(HttpStatus.OK);
}
@@ -98,7 +100,7 @@ public class DataRestController implements CpsDataApi {
public ResponseEntity<Object> replaceNode(final String dataspaceName, final String anchorName,
final Object jsonData, final String parentNodeXpath, final String observedTimestamp) {
cpsDataService
- .replaceNodeTree(dataspaceName, anchorName, parentNodeXpath, jsonData.toString(),
+ .replaceNodeTree(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData),
toOffsetDateTime(observedTimestamp));
return new ResponseEntity<>(HttpStatus.OK);
}
@@ -107,7 +109,7 @@ public class DataRestController implements CpsDataApi {
public ResponseEntity<Object> replaceListContent(final String parentNodeXpath,
final String dataspaceName, final String anchorName, final Object jsonData,
final String observedTimestamp) {
- cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, jsonData.toString(),
+ cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData),
toOffsetDateTime(observedTimestamp));
return new ResponseEntity<>(HttpStatus.OK);
}
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 445b2a2bd..fbb563656 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
@@ -60,8 +60,8 @@ class DataRestControllerSpec extends Specification {
def dataspaceName = 'my_dataspace'
def anchorName = 'my_anchor'
def noTimestamp = null
- def jsonString = '{"some-key" : "some-value"}'
- def jsonObject
+ def requestBody = '{"some-key" : "some-value","categories":[{"books":[{"authors":["Iain M. Banks"]}]}]}'
+ def expectedJsonData = '{"some-key":"some-value","categories":[{"books":[{"authors":["Iain M. Banks"]}]}]}'
@Shared
static DataNode dataNodeWithLeavesNoChildren = new DataNodeBuilder().withXpath('/xpath')
@@ -73,7 +73,6 @@ class DataRestControllerSpec extends Specification {
def setup() {
dataNodeBaseEndpoint = "$basePath/v1/dataspaces/$dataspaceName"
- jsonObject = groovy.json.JsonOutput.toJson(jsonString);
}
def 'Create a node: #scenario.'() {
@@ -85,12 +84,12 @@ class DataRestControllerSpec extends Specification {
post(endpoint)
.contentType(MediaType.APPLICATION_JSON)
.param('xpath', parentNodeXpath)
- .content(jsonObject)
+ .content(requestBody)
).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, jsonString, noTimestamp)
+ 1 * mockCpsDataService.saveData(dataspaceName, anchorName, expectedJsonData, noTimestamp)
where: 'following xpath parameters are are used'
scenario | parentNodeXpath
'no xpath parameter' | ''
@@ -107,12 +106,12 @@ class DataRestControllerSpec extends Specification {
.contentType(MediaType.APPLICATION_JSON)
.param('xpath', '')
.param('observed-timestamp', observedTimestamp)
- .content(jsonObject)
+ .content(requestBody)
).andReturn().response
then: 'a created response is returned'
response.status == expectedHttpStatus.value()
then: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.saveData(dataspaceName, anchorName, jsonString,
+ expectedApiCount * mockCpsDataService.saveData(dataspaceName, anchorName, expectedJsonData,
{ it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
where:
scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
@@ -129,7 +128,7 @@ class DataRestControllerSpec extends Specification {
def postRequestBuilder = post(endpoint)
.contentType(MediaType.APPLICATION_JSON)
.param('xpath', parentNodeXpath)
- .content(jsonObject)
+ .content(requestBody)
if (observedTimestamp != null)
postRequestBuilder.param('observed-timestamp', observedTimestamp)
def response =
@@ -137,7 +136,7 @@ class DataRestControllerSpec extends Specification {
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, jsonString,
+ 1 * mockCpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, expectedJsonData,
DateTimeUtility.toOffsetDateTime(observedTimestamp))
where:
scenario | observedTimestamp
@@ -152,14 +151,14 @@ class DataRestControllerSpec extends Specification {
def postRequestBuilder = post("$dataNodeBaseEndpoint/anchors/$anchorName/list-nodes")
.contentType(MediaType.APPLICATION_JSON)
.param('xpath', parentNodeXpath)
- .content(jsonObject)
+ .content(requestBody)
if (observedTimestamp != null)
postRequestBuilder.param('observed-timestamp', observedTimestamp)
def response = mvc.perform(postRequestBuilder).andReturn().response
then: 'a created response is returned'
response.status == expectedHttpStatus.value()
then: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, jsonString,
+ expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, expectedJsonData,
{ it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
where:
scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
@@ -216,11 +215,11 @@ class DataRestControllerSpec extends Specification {
mvc.perform(
patch(endpoint)
.contentType(MediaType.APPLICATION_JSON)
- .content(jsonObject)
+ .content(requestBody)
.param('xpath', inputXpath)
).andReturn().response
then: 'the service method is invoked with expected parameters'
- 1 * mockCpsDataService.updateNodeLeaves(dataspaceName, anchorName, xpathServiceParameter, jsonString, null)
+ 1 * mockCpsDataService.updateNodeLeaves(dataspaceName, anchorName, xpathServiceParameter, expectedJsonData, null)
and: 'response status indicates success'
response.status == HttpStatus.OK.value()
where:
@@ -238,12 +237,12 @@ class DataRestControllerSpec extends Specification {
mvc.perform(
patch(endpoint)
.contentType(MediaType.APPLICATION_JSON)
- .content(jsonObject)
+ .content(requestBody)
.param('xpath', '/')
.param('observed-timestamp', observedTimestamp)
).andReturn().response
then: 'the service method is invoked with expected parameters'
- expectedApiCount * mockCpsDataService.updateNodeLeaves(dataspaceName, anchorName, '/', jsonString,
+ expectedApiCount * mockCpsDataService.updateNodeLeaves(dataspaceName, anchorName, '/', expectedJsonData,
{ it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
and: 'response status indicates success'
response.status == expectedHttpStatus.value()
@@ -261,11 +260,11 @@ class DataRestControllerSpec extends Specification {
mvc.perform(
put(endpoint)
.contentType(MediaType.APPLICATION_JSON)
- .content(jsonObject)
+ .content(requestBody)
.param('xpath', inputXpath))
.andReturn().response
then: 'the service method is invoked with expected parameters'
- 1 * mockCpsDataService.replaceNodeTree(dataspaceName, anchorName, xpathServiceParameter, jsonString, noTimestamp)
+ 1 * mockCpsDataService.replaceNodeTree(dataspaceName, anchorName, xpathServiceParameter, expectedJsonData, noTimestamp)
and: 'response status indicates success'
response.status == HttpStatus.OK.value()
where:
@@ -283,12 +282,12 @@ class DataRestControllerSpec extends Specification {
mvc.perform(
put(endpoint)
.contentType(MediaType.APPLICATION_JSON)
- .content(jsonObject)
+ .content(requestBody)
.param('xpath', '')
.param('observed-timestamp', observedTimestamp))
.andReturn().response
then: 'the service method is invoked with expected parameters'
- expectedApiCount * mockCpsDataService.replaceNodeTree(dataspaceName, anchorName, '/', jsonString,
+ expectedApiCount * mockCpsDataService.replaceNodeTree(dataspaceName, anchorName, '/', expectedJsonData,
{ it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
and: 'response status indicates success'
response.status == expectedHttpStatus.value()
@@ -303,14 +302,14 @@ class DataRestControllerSpec extends Specification {
def putRequestBuilder = put("$dataNodeBaseEndpoint/anchors/$anchorName/list-nodes")
.contentType(MediaType.APPLICATION_JSON)
.param('xpath', 'parent xpath')
- .content(jsonObject)
+ .content(requestBody)
if (observedTimestamp != null)
putRequestBuilder.param('observed-timestamp', observedTimestamp)
def response = mvc.perform(putRequestBuilder).andReturn().response
then: 'a success response is returned'
response.status == expectedHttpStatus.value()
and: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.replaceListContent(dataspaceName, anchorName, 'parent xpath', jsonString,
+ expectedApiCount * mockCpsDataService.replaceListContent(dataspaceName, anchorName, 'parent xpath', expectedJsonData,
{ it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
where:
scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy
index cca2ac621..25b90d702 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy
@@ -88,4 +88,31 @@ class YangUtilsSpec extends Specification {
'another invalid parent path' | '/test-tree/branch[@name=\'Branch\']/nest/name/last-name'
'fragment does not belong to parent' | '/test-tree/'
}
+
+ def 'Parsing json data with invalid json string: #description.'() {
+ given: 'schema context'
+ def yangResourcesMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
+ when: 'malformed json string is parsed'
+ YangUtils.parseJsonData(invalidJson, schemaContext)
+ then: 'an exception is thrown'
+ thrown(DataValidationException)
+ where: 'the following malformed json is provided'
+ description | invalidJson
+ 'malformed json string with unterminated array data' | '{bookstore={categories=[{books=[{authors=[Iain M. Banks]}]}]}}'
+ 'incorrect json' | '{" }'
+ }
+
+ def 'Parsing json data with space.'() {
+ given: 'schema context'
+ def yangResourcesMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
+ and: 'some json data with space in the array elements'
+ def jsonDataWithSpacesInArrayElement = TestUtils.getResourceFileContent('bookstore.json')
+ when: 'that json data is parsed'
+ YangUtils.parseJsonData(jsonDataWithSpacesInArrayElement, schemaContext)
+ then: 'no exception thrown'
+ noExceptionThrown()
+ }
+
}
diff --git a/csit/data/test-tree.json b/csit/data/test-tree.json
index bc9cbd7ce..89d678427 100644
--- a/csit/data/test-tree.json
+++ b/csit/data/test-tree.json
@@ -17,7 +17,7 @@
"nest": {
"name": "Big",
"birds": [
- "Owl",
+ "Night Owl",
"Raven",
"Crow"
]