diff options
author | Rudrangi Anupriya <ra00745022@techmahindra.com> | 2024-12-02 15:37:07 +0530 |
---|---|---|
committer | Rudrangi Anupriya <ra00745022@techmahindra.com> | 2024-12-12 07:18:23 +0000 |
commit | 7bc7ca3004adc6a7c1bbcee62e32e1906f2976d1 (patch) | |
tree | 6621a8adc49182c5d04738104df201e85dd1d8f4 | |
parent | 29061930922eeb686da7f91ec7852b7ee875c739 (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>
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: |