aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRudrangi Anupriya <ra00745022@techmahindra.com>2024-12-02 15:37:07 +0530
committerRudrangi Anupriya <ra00745022@techmahindra.com>2024-12-12 07:18:23 +0000
commit7bc7ca3004adc6a7c1bbcee62e32e1906f2976d1 (patch)
tree6621a8adc49182c5d04738104df201e85dd1d8f4
parent29061930922eeb686da7f91ec7852b7ee875c739 (diff)
Implementation of Data validation feature in CPS APIs
Added support to validate JSON/XML data without the need of persisting it in the database. - added "dryRunInQuery" flag as a new query parameter in update/Replace/Add APIs - added new method as part of CpsDataService layer to perform data validation Issue-ID: CPS-2516 Change-Id: I87bb33dd6021567d0fac606d5c4b0168d107311c Signed-off-by: Rudrangi Anupriya <ra00745022@techmahindra.com>
-rw-r--r--cps-rest/docs/openapi/components.yml2
-rw-r--r--cps-rest/docs/openapi/cpsData.yml4
-rwxr-xr-xcps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java61
-rwxr-xr-xcps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy95
-rw-r--r--docs/api/swagger/cps/openapi.yaml56
5 files changed, 180 insertions, 38 deletions
diff --git a/cps-rest/docs/openapi/components.yml b/cps-rest/docs/openapi/components.yml
index 1db4185330..1a7e4308d9 100644
--- a/cps-rest/docs/openapi/components.yml
+++ b/cps-rest/docs/openapi/components.yml
@@ -326,7 +326,7 @@ components:
dryRunInQuery:
name: dry-run
in: query
- description: Boolean flag to validate data, without persisting it. Default value is set to false.
+ description: Boolean flag to validate data, without persisting it. Default value is false.
required: false
schema:
type: boolean
diff --git a/cps-rest/docs/openapi/cpsData.yml b/cps-rest/docs/openapi/cpsData.yml
index 36000fd7d8..178a68fb77 100644
--- a/cps-rest/docs/openapi/cpsData.yml
+++ b/cps-rest/docs/openapi/cpsData.yml
@@ -31,6 +31,7 @@ listElementByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/requiredXpathInQuery'
+ - $ref: 'components.yml#/components/parameters/dryRunInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
- $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
@@ -70,6 +71,7 @@ listElementByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/requiredXpathInQuery'
+ - $ref: 'components.yml#/components/parameters/dryRunInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
- $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
@@ -154,6 +156,7 @@ nodesByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
+ - $ref: 'components.yml#/components/parameters/dryRunInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
- $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
@@ -214,6 +217,7 @@ nodesByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
+ - $ref: 'components.yml#/components/parameters/dryRunInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
- $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
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 d460f52415..be552ecc6a 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
@@ -102,12 +102,17 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<String> addListElements(final String apiVersion, final String dataspaceName,
final String anchorName, final String parentNodeXpath,
- final String nodeData, final String observedTimestamp,
- final String contentTypeInHeader) {
+ final String nodeData, final Boolean dryRunEnabled,
+ final String observedTimestamp, final String contentTypeInHeader) {
final ContentType contentType = ContentType.fromString(contentTypeInHeader);
- cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath,
- nodeData, toOffsetDateTime(observedTimestamp), contentType);
- return new ResponseEntity<>(HttpStatus.CREATED);
+ if (Boolean.TRUE.equals(dryRunEnabled)) {
+ cpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType);
+ return ResponseEntity.ok().build();
+ } else {
+ cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath,
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
+ }
+ return ResponseEntity.status(HttpStatus.CREATED).build();
}
@Override
@@ -151,34 +156,50 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<Object> updateNodeLeaves(final String apiVersion, final String dataspaceName,
final String anchorName, final String nodeData,
- final String parentNodeXpath, final String observedTimestamp,
- final String contentTypeInHeader) {
+ final String parentNodeXpath, final Boolean dryRunEnabled,
+ final String observedTimestamp, final String contentTypeInHeader) {
final ContentType contentType = ContentType.fromString(contentTypeInHeader);
- cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath,
- nodeData, toOffsetDateTime(observedTimestamp), contentType);
- return new ResponseEntity<>(HttpStatus.OK);
+ if (Boolean.TRUE.equals(dryRunEnabled)) {
+ cpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType);
+ return ResponseEntity.ok().build();
+ } else {
+ cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath,
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
+ }
+ return ResponseEntity.status(HttpStatus.OK).build();
}
@Override
public ResponseEntity<Object> replaceNode(final String apiVersion, final String dataspaceName,
final String anchorName, final String nodeData,
- final String parentNodeXpath, final String observedTimestamp,
- final String contentTypeInHeader) {
+ final String parentNodeXpath, final Boolean dryRunEnabled,
+ final String observedTimestamp, final String contentTypeInHeader) {
final ContentType contentType = ContentType.fromString(contentTypeInHeader);
- cpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath,
- nodeData, toOffsetDateTime(observedTimestamp), contentType);
- return new ResponseEntity<>(HttpStatus.OK);
+ if (Boolean.TRUE.equals(dryRunEnabled)) {
+ cpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType);
+ return ResponseEntity.ok().build();
+ } else {
+ cpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath,
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
+ }
+ return ResponseEntity.status(HttpStatus.OK).build();
}
@Override
public ResponseEntity<Object> replaceListContent(final String apiVersion, final String dataspaceName,
final String anchorName, final String parentNodeXpath,
- final String nodeData, final String observedTimestamp,
- final String contentTypeInHeader) {
+ final String nodeData, final Boolean dryRunEnabled,
+ final String observedTimestamp, final String contentTypeInHeader) {
final ContentType contentType = ContentType.fromString(contentTypeInHeader);
- cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath,
- nodeData, toOffsetDateTime(observedTimestamp), contentType);
- return new ResponseEntity<>(HttpStatus.OK);
+ if (Boolean.TRUE.equals(dryRunEnabled)) {
+ cpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, nodeData,
+ ContentType.JSON);
+ return ResponseEntity.ok().build();
+ } else {
+ cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath,
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
+ }
+ return ResponseEntity.status(HttpStatus.OK).build();
}
@Override
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 915fbdecf5..ca89fafe83 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
@@ -168,16 +168,17 @@ class DataRestControllerSpec extends Specification {
given: 'an endpoint to create a node'
def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
def parentNodeXpath = '/'
+ and: 'dryRunEnabled flag is set to true'
def dryRunEnabled = 'true'
when: 'post is invoked with json data and dry-run flag enabled'
def response =
- mvc.perform(
- post(endpoint)
- .contentType(MediaType.APPLICATION_JSON)
- .param('xpath', parentNodeXpath)
- .param('dry-run', dryRunEnabled)
- .content(requestBodyJson)
- ).andReturn().response
+ mvc.perform(
+ post(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .param('xpath', parentNodeXpath)
+ .param('dry-run', dryRunEnabled)
+ .content(requestBodyJson)
+ ).andReturn().response
then: 'a 200 OK response is returned'
response.status == HttpStatus.OK.value()
then: 'the service was called with correct parameters'
@@ -263,6 +264,26 @@ class DataRestControllerSpec extends Specification {
'Content type XML with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_XML | requestBodyXml || 0 | HttpStatus.BAD_REQUEST | expectedXmlData | ContentType.XML
}
+ def 'Validate data using Save list elements API'() {
+ given: 'endpoint to save list elements'
+ def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes"
+ and: 'dryRunEnabled flag is set to true'
+ def dryRunEnabled = 'true'
+ when: 'post request is performed'
+ def response =
+ mvc.perform(
+ post(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .param('xpath', '/')
+ .content(requestBodyJson)
+ .param('dry-run', dryRunEnabled)
+ ).andReturn().response
+ then: 'a 200 OK response is returned'
+ response.status == HttpStatus.OK.value()
+ then: 'the service was called with correct parameters'
+ 1 * mockCpsDataService.validateData(dataspaceName, anchorName, '/', requestBodyJson, ContentType.JSON)
+ }
+
def 'Get data node with leaves'() {
given: 'the service returns data node leaves'
def xpath = 'parent-1'
@@ -515,6 +536,26 @@ class DataRestControllerSpec extends Specification {
'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST
}
+ def 'Validate data using Update a node API'() {
+ given: 'endpoint to update a node leaves'
+ def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
+ and: 'dryRunEnabled flag is set to true'
+ def dryRunEnabled = 'true'
+ when: 'patch request is performed'
+ def response =
+ mvc.perform(
+ patch(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBodyJson)
+ .param('xpath', '/')
+ .param('dry-run', dryRunEnabled)
+ ).andReturn().response
+ then: 'a 200 OK response is returned'
+ response.status == HttpStatus.OK.value()
+ then: 'the service was called with correct parameters'
+ 1 * mockCpsDataService.validateData(dataspaceName, anchorName, '/', requestBodyJson, ContentType.JSON)
+ }
+
def 'Replace data node tree: #scenario.'() {
given: 'endpoint to replace node'
def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
@@ -540,6 +581,26 @@ class DataRestControllerSpec extends Specification {
'XML content: some xpath by parent' | '/some/xpath' | MediaType.APPLICATION_XML || '/some/xpath' | requestBodyXml | expectedXmlData | ContentType.XML
}
+ def 'Validate data using Replace data node API'() {
+ given: 'endpoint to replace node'
+ def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
+ and: 'dryRunEnabled flag is set to true'
+ def dryRunEnabled = 'true'
+ when: 'put request is performed'
+ def response =
+ mvc.perform(
+ put(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBodyJson)
+ .param('xpath', '/')
+ .param('dry-run', dryRunEnabled)
+ ).andReturn().response
+ then: 'a 200 OK response is returned'
+ response.status == HttpStatus.OK.value()
+ then: 'the service was called with correct parameters'
+ 1 * mockCpsDataService.validateData(dataspaceName, anchorName, '/', requestBodyJson, ContentType.JSON)
+ }
+
def 'Update data node and descendants with observedTimestamp.'() {
given: 'endpoint to replace node'
def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
@@ -605,6 +666,26 @@ class DataRestControllerSpec extends Specification {
'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST
}
+ def 'Validate data using Replace list content API'() {
+ given: 'endpoint to replace list-nodes'
+ def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes"
+ and: 'dryRunEnabled flag is set to true'
+ def dryRunEnabled = 'true'
+ when: 'put request is performed'
+ def response =
+ mvc.perform(
+ put(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .param('xpath', '/')
+ .content(requestBodyJson)
+ .param('dry-run', dryRunEnabled)
+ ).andReturn().response
+ then: 'a 200 OK response is returned'
+ response.status == HttpStatus.OK.value()
+ then: 'the service was called with correct parameters'
+ 1 * mockCpsDataService.validateData(dataspaceName, anchorName, '/', requestBodyJson, ContentType.JSON)
+ }
+
def 'Delete list element #scenario.'() {
when: 'list-nodes endpoint is invoked with delete operation'
def deleteRequestBuilder = delete("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes")
diff --git a/docs/api/swagger/cps/openapi.yaml b/docs/api/swagger/cps/openapi.yaml
index 7a300207cf..c84609b638 100644
--- a/docs/api/swagger/cps/openapi.yaml
+++ b/docs/api/swagger/cps/openapi.yaml
@@ -1354,6 +1354,15 @@ paths:
schema:
default: /
type: string
+ - description: "Boolean flag to validate data, without persisting it. Default\
+ \ value is false."
+ in: query
+ name: dry-run
+ required: false
+ schema:
+ default: false
+ example: false
+ type: boolean
- description: observed-timestamp
in: query
name: observed-timestamp
@@ -1474,7 +1483,7 @@ paths:
default: /
type: string
- description: "Boolean flag to validate data, without persisting it. Default\
- \ value is set to false."
+ \ value is false."
in: query
name: dry-run
required: false
@@ -1610,6 +1619,15 @@ paths:
schema:
default: /
type: string
+ - description: "Boolean flag to validate data, without persisting it. Default\
+ \ value is false."
+ in: query
+ name: dry-run
+ required: false
+ schema:
+ default: false
+ example: false
+ type: boolean
- description: observed-timestamp
in: query
name: observed-timestamp
@@ -1804,6 +1822,15 @@ paths:
required: true
schema:
type: string
+ - description: "Boolean flag to validate data, without persisting it. Default\
+ \ value is false."
+ in: query
+ name: dry-run
+ required: false
+ schema:
+ default: false
+ example: false
+ type: boolean
- description: observed-timestamp
in: query
name: observed-timestamp
@@ -1920,6 +1947,15 @@ paths:
required: true
schema:
type: string
+ - description: "Boolean flag to validate data, without persisting it. Default\
+ \ value is false."
+ in: query
+ name: dry-run
+ required: false
+ schema:
+ default: false
+ example: false
+ type: boolean
- description: observed-timestamp
in: query
name: observed-timestamp
@@ -2623,17 +2659,9 @@ components:
- application/json
- application/xml
type: string
- observedTimestampInQuery:
- description: observed-timestamp
- in: query
- name: observed-timestamp
- required: false
- schema:
- example: 2021-03-21T00:10:34.030-0100
- type: string
dryRunInQuery:
description: "Boolean flag to validate data, without persisting it. Default\
- \ value is set to false."
+ \ value is false."
in: query
name: dry-run
required: false
@@ -2641,6 +2669,14 @@ components:
default: false
example: false
type: boolean
+ observedTimestampInQuery:
+ description: observed-timestamp
+ in: query
+ name: observed-timestamp
+ required: false
+ schema:
+ example: 2021-03-21T00:10:34.030-0100
+ type: string
requiredXpathInQuery:
description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/xpath.html"
examples: