From e140c60532de6487225ee7860ade0825cb350648 Mon Sep 17 00:00:00 2001 From: Arpit Singh Date: Wed, 2 Aug 2023 18:35:31 +0530 Subject: CPS Delta API 1: Delta between 2 anchors - CPS Delta Feature Part 1: To find delta between two anchors - created new endpoint deltaByDataspaceAndAnchors - endpoint to take dataspaceName, source anchor, target anchor, xpath, descendants as input - added new service CpsDeltaService - added method to find delta between DataNodes: getDeltaReport - added method to find removed data nodes: getRemovedDeltaReports - added method to get Added DataNodes: getAddedDeltaReports - added method to get Map of xpath to DataNode: convertToXPathToDataNodesMap - added a POJO for delta report - Added new JSON data for delta feature testing - Added groovy test files CpsDeltaServiceImplSpec and DeltaReportBuilderSpec - code related to update operation, will be added in separate commit Issue-ID: CPS-1824 Signed-off-by: Arpit Singh Change-Id: I313f0f71d04b03878be7643f709d8af1aa6df6ba --- cps-rest/docs/openapi/components.yml | 26 +++++++++++++++++ cps-rest/docs/openapi/cpsDataV2.yml | 33 ++++++++++++++++++++++ cps-rest/docs/openapi/openapi.yml | 3 ++ .../cps/rest/controller/DataRestController.java | 18 ++++++++++++ .../rest/controller/DataRestControllerSpec.groovy | 21 +++++++++++++- 5 files changed, 100 insertions(+), 1 deletion(-) (limited to 'cps-rest') diff --git a/cps-rest/docs/openapi/components.yml b/cps-rest/docs/openapi/components.yml index a3016ce76..c1b111bfa 100644 --- a/cps-rest/docs/openapi/components.yml +++ b/cps-rest/docs/openapi/components.yml @@ -137,6 +137,24 @@ components: name: SciFi - code: 02 name: kids + deltaReportSample: + value: + - action: "ADD" + xpath: "/bookstore/categories/[@code=3]" + target-data: + code: 3, + name: "kidz" + - action: "REMOVE" + xpath: "/bookstore/categories/[@code=1]" + source-data: + code: 1, + name: "Fiction" + - action: "UPDATE" + xpath: "/bookstore/categories/[@code=2]" + source-data: + name: "Funny" + target-data: + name: "Comic" parameters: dataspaceNameInQuery: @@ -187,6 +205,14 @@ components: schema: type: string example: my-anchor + targetAnchorNameInQuery: + name: target-anchor-name + in: query + description: target-anchor-name + required: true + schema: + type: string + example: my-anchor xpathInQuery: name: xpath in: query diff --git a/cps-rest/docs/openapi/cpsDataV2.yml b/cps-rest/docs/openapi/cpsDataV2.yml index ad0c299d7..c7629b70e 100644 --- a/cps-rest/docs/openapi/cpsDataV2.yml +++ b/cps-rest/docs/openapi/cpsDataV2.yml @@ -46,4 +46,37 @@ nodeByDataspaceAndAnchor: $ref: 'components.yml#/components/responses/Forbidden' '500': $ref: 'components.yml#/components/responses/InternalServerError' + x-codegen-request-body-name: xpath + +deltaByDataspaceAndAnchors: + get: + description: Get delta between two anchors within a given dataspace + tags: + - cps-data + summary: Get delta between anchors in the same dataspace + operationId: getDeltaByDataspaceAndAnchors + parameters: + - $ref: 'components.yml#/components/parameters/dataspaceNameInPath' + - $ref: 'components.yml#/components/parameters/anchorNameInPath' + - $ref: 'components.yml#/components/parameters/targetAnchorNameInQuery' + - $ref: 'components.yml#/components/parameters/xpathInQuery' + - $ref: 'components.yml#/components/parameters/descendantsInQuery' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + examples: + dataSample: + $ref: 'components.yml#/components/examples/deltaReportSample' + '400': + $ref: 'components.yml#/components/responses/BadRequest' + '401': + $ref: 'components.yml#/components/responses/Unauthorized' + '403': + $ref: 'components.yml#/components/responses/Forbidden' + '500': + $ref: 'components.yml#/components/responses/InternalServerError' x-codegen-request-body-name: xpath \ No newline at end of file diff --git a/cps-rest/docs/openapi/openapi.yml b/cps-rest/docs/openapi/openapi.yml index 4bbf9f0fb..f29335a0a 100644 --- a/cps-rest/docs/openapi/openapi.yml +++ b/cps-rest/docs/openapi/openapi.yml @@ -104,6 +104,9 @@ paths: /{apiVersion}/dataspaces/{dataspace-name}/anchors/{anchor-name}/list-nodes: $ref: 'cpsData.yml#/listElementByDataspaceAndAnchor' + /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/delta: + $ref: 'cpsDataV2.yml#/deltaByDataspaceAndAnchors' + /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes/query: $ref: 'cpsQueryV1Deprecated.yml#/nodesByDataspaceAndAnchorAndCpsPath' 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 60e7fb6d2..4f9328b6c 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 @@ -38,6 +38,7 @@ import org.onap.cps.api.CpsDataService; import org.onap.cps.rest.api.CpsDataApi; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; +import org.onap.cps.spi.model.DeltaReport; import org.onap.cps.utils.ContentType; import org.onap.cps.utils.DataMapUtils; import org.onap.cps.utils.JsonObjectMapper; @@ -166,6 +167,23 @@ public class DataRestController implements CpsDataApi { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + @Override + @Timed(value = "cps.data.controller.get.delta", + description = "Time taken to get delta between anchors") + public ResponseEntity getDeltaByDataspaceAndAnchors(final String dataspaceName, + final String sourceAnchorName, + final String targetAnchorName, + final String xpath, + final String descendants) { + final FetchDescendantsOption fetchDescendantsOption = + FetchDescendantsOption.getFetchDescendantsOption(descendants); + + final List deltaBetweenAnchors = + cpsDataService.getDeltaByDataspaceAndAnchors(dataspaceName, sourceAnchorName, + targetAnchorName, xpath, fetchDescendantsOption); + return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), 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 81262c80c..12c9c4c60 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 @@ -30,6 +30,7 @@ import org.onap.cps.api.CpsDataService import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.spi.model.DeltaReportBuilder import org.onap.cps.utils.ContentType import org.onap.cps.utils.DateTimeUtility import org.onap.cps.utils.JsonObjectMapper @@ -331,7 +332,25 @@ class DataRestControllerSpec extends Specification { and: 'the response contains the root node identifier' assert response.contentAsString.contains('parent') and: 'the response contains child is true' - assert response.contentAsString.contains('"child"') == true + assert response.contentAsString.contains('"child"') + } + + def 'Get delta between two anchors'() { + given: 'the service returns a list containing delta reports' + def deltaReports = new DeltaReportBuilder().actionAdd().withXpath('/bookstore').withSourceData('bookstore-name': 'Easons').withTargetData('bookstore-name': 'Easons').build() + def xpath = 'some xpath' + def endpoint = "$dataNodeBaseEndpointV2/anchors/sourceAnchor/delta" + mockCpsDataService.getDeltaByDataspaceAndAnchors(dataspaceName, 'sourceAnchor', 'targetAnchor', xpath, OMIT_DESCENDANTS) >> [deltaReports] + when: 'get delta request is performed using REST API' + def response = + mvc.perform(get(endpoint) + .param('target-anchor-name', 'targetAnchor') + .param('xpath', xpath)) + .andReturn().response + then: 'expected response code is returned' + assert response.status == HttpStatus.OK.value() + and: 'the response contains expected value' + assert response.contentAsString.contains("[{\"action\":\"add\",\"xpath\":\"/bookstore\",\"sourceData\":{\"bookstore-name\":\"Easons\"},\"targetData\":{\"bookstore-name\":\"Easons\"}}]") } def 'Update data node leaves: #scenario.'() { -- cgit 1.2.3-korg