diff options
Diffstat (limited to 'cps-rest')
8 files changed, 325 insertions, 193 deletions
diff --git a/cps-rest/docs/openapi/cpsDataV2.yml b/cps-rest/docs/openapi/cpsDataV2.yml index 999c5b2c19..7afda705f7 100644 --- a/cps-rest/docs/openapi/cpsDataV2.yml +++ b/cps-rest/docs/openapi/cpsDataV2.yml @@ -1,5 +1,5 @@ # ============LICENSE_START======================================================= -# Copyright (c) 2022-2024 TechMahindra Ltd. +# Copyright (c) 2022-2025 TechMahindra Ltd. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,83 +54,3 @@ nodeByDataspaceAndAnchor: '500': $ref: 'components.yml#/components/responses/InternalServerError' x-codegen-request-body-name: xpath - -delta: - 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/sourceAnchorNameInPath' - - $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' - '403': - $ref: 'components.yml#/components/responses/Forbidden' - '500': - $ref: 'components.yml#/components/responses/InternalServerError' - x-codegen-request-body-name: xpath - post: - description: Get delta between an anchor in a dataspace and JSON payload - tags: - - cps-data - summary: Get delta between an anchor and JSON payload - operationId: getDeltaByDataspaceAnchorAndPayload - parameters: - - $ref: 'components.yml#/components/parameters/dataspaceNameInPath' - - $ref: 'components.yml#/components/parameters/sourceAnchorNameInPath' - - $ref: 'components.yml#/components/parameters/xpathInQuery' - requestBody: - content: - multipart/form-data: - schema: - type: object - properties: - json: - type: object - example: - test:bookstore: - bookstore-name: Chapters - categories: - - code: 01 - name: SciFi - - code: 02 - name: kids - file: - type: string - format: binary - required: - - json - 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'
\ No newline at end of file diff --git a/cps-rest/docs/openapi/cpsDelta.yml b/cps-rest/docs/openapi/cpsDelta.yml new file mode 100644 index 0000000000..67535ce832 --- /dev/null +++ b/cps-rest/docs/openapi/cpsDelta.yml @@ -0,0 +1,97 @@ +# ============LICENSE_START======================================================= +# Copyright (c) 2025 TechMahindra Ltd. +# ================================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END========================================================= + +delta: + get: + description: Get delta between two anchors within a given dataspace + tags: + - cps-delta + summary: Get delta between anchors in the same dataspace + operationId: getDeltaByDataspaceAndAnchors + parameters: + - $ref: 'components.yml#/components/parameters/dataspaceNameInPath' + - $ref: 'components.yml#/components/parameters/sourceAnchorNameInPath' + - $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' + '403': + $ref: 'components.yml#/components/responses/Forbidden' + '500': + $ref: 'components.yml#/components/responses/InternalServerError' + x-codegen-request-body-name: xpath + post: + description: Get delta between an anchor in a dataspace and JSON payload + tags: + - cps-delta + summary: Get delta between an anchor and JSON payload + operationId: getDeltaByDataspaceAnchorAndPayload + parameters: + - $ref: 'components.yml#/components/parameters/dataspaceNameInPath' + - $ref: 'components.yml#/components/parameters/sourceAnchorNameInPath' + - $ref: 'components.yml#/components/parameters/xpathInQuery' + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + json: + type: object + example: + test:bookstore: + bookstore-name: Chapters + categories: + - code: 01 + name: SciFi + - code: 02 + name: kids + file: + type: string + format: binary + required: + - json + 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' diff --git a/cps-rest/docs/openapi/openapi.yml b/cps-rest/docs/openapi/openapi.yml index 09c454b1da..c3f3a0257a 100644 --- a/cps-rest/docs/openapi/openapi.yml +++ b/cps-rest/docs/openapi/openapi.yml @@ -44,6 +44,8 @@ tags: description: cps Admin - name: cps-data description: cps Data + - name: cps-delta + description: CPS Delta paths: /v1/dataspaces: @@ -104,7 +106,7 @@ paths: $ref: 'cpsData.yml#/listElementByDataspaceAndAnchor' /v2/dataspaces/{dataspace-name}/anchors/{source-anchor-name}/delta: - $ref: 'cpsDataV2.yml#/delta' + $ref: 'cpsDelta.yml#/delta' /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 b6a2e42a14..90500f3955 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 @@ -3,7 +3,7 @@ * Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2025 Nordix Foundation - * Modifications Copyright (C) 2022-2024 TechMahindra Ltd. + * Modifications Copyright (C) 2022-2025 TechMahindra Ltd. * Modifications Copyright (C) 2022 Deutsche Telekom AG * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,21 +24,16 @@ package org.onap.cps.rest.controller; -import static org.onap.cps.rest.utils.MultipartFileUtil.extractYangResourcesMap; - import io.micrometer.core.annotation.Timed; import jakarta.validation.ValidationException; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; -import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsFacade; -import org.onap.cps.api.model.DeltaReport; import org.onap.cps.api.parameters.FetchDescendantsOption; import org.onap.cps.rest.api.CpsDataApi; import org.onap.cps.utils.ContentType; @@ -48,7 +43,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("${rest.api.cps-base-path}") @@ -193,44 +187,6 @@ public class DataRestController implements CpsDataApi { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } - @Override - public ResponseEntity<Object> getDeltaByDataspaceAnchorAndPayload(final String dataspaceName, - final String sourceAnchorName, - final Object jsonPayload, - final String xpath, - final MultipartFile multipartFile) { - final FetchDescendantsOption fetchDescendantsOption = FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; - - final Map<String, String> yangResourceMap; - if (multipartFile == null) { - yangResourceMap = Collections.emptyMap(); - } else { - yangResourceMap = extractYangResourcesMap(multipartFile); - } - final Collection<DeltaReport> deltaReports = Collections.unmodifiableList( - cpsDataService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, sourceAnchorName, - xpath, yangResourceMap, jsonPayload.toString(), fetchDescendantsOption)); - - return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaReports), HttpStatus.OK); - } - - @Override - @Timed(value = "cps.data.controller.get.delta", - description = "Time taken to get delta between anchors") - public ResponseEntity<Object> getDeltaByDataspaceAndAnchors(final String dataspaceName, - final String sourceAnchorName, - final String targetAnchorName, - final String xpath, - final String descendants) { - final FetchDescendantsOption fetchDescendantsOption = - FetchDescendantsOption.getFetchDescendantsOption(descendants); - - final List<DeltaReport> deltaBetweenAnchors = - cpsDataService.getDeltaByDataspaceAndAnchors(dataspaceName, sourceAnchorName, - targetAnchorName, xpath, fetchDescendantsOption); - return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), HttpStatus.OK); - } - private ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataMaps, final ContentType contentType) { final String responseData; diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DeltaRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DeltaRestController.java new file mode 100644 index 0000000000..f27346cfa7 --- /dev/null +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DeltaRestController.java @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2025 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.rest.controller; + +import static org.onap.cps.rest.utils.MultipartFileUtil.extractYangResourcesMap; + +import io.micrometer.core.annotation.Timed; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.onap.cps.api.CpsDeltaService; +import org.onap.cps.api.model.DeltaReport; +import org.onap.cps.api.parameters.FetchDescendantsOption; +import org.onap.cps.rest.api.CpsDeltaApi; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("${rest.api.cps-base-path}") +@RequiredArgsConstructor +public class DeltaRestController implements CpsDeltaApi { + + private final CpsDeltaService cpsDeltaService; + private final JsonObjectMapper jsonObjectMapper; + + + @Timed(value = "cps.delta.controller.get.delta", + description = "Time taken to get delta between anchors") + @Override + public ResponseEntity<Object> getDeltaByDataspaceAndAnchors(final String dataspaceName, + final String sourceAnchorName, + final String targetAnchorName, + final String xpath, + final String descendants) { + final FetchDescendantsOption fetchDescendantsOption = + FetchDescendantsOption.getFetchDescendantsOption(descendants); + final List<DeltaReport> deltaBetweenAnchors = + cpsDeltaService.getDeltaByDataspaceAndAnchors(dataspaceName, sourceAnchorName, + targetAnchorName, xpath, fetchDescendantsOption); + return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), HttpStatus.OK); + } + + @Timed(value = "cps.delta.controller.get.delta", + description = "Time taken to get delta between anchors") + @Override + public ResponseEntity<Object> getDeltaByDataspaceAnchorAndPayload(final String dataspaceName, + final String sourceAnchorName, + final Object jsonPayload, + final String xpath, + final MultipartFile multipartFile) { + final FetchDescendantsOption fetchDescendantsOption = FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; + + final Map<String, String> yangResourceMap; + if (multipartFile == null) { + yangResourceMap = Collections.emptyMap(); + } else { + yangResourceMap = extractYangResourcesMap(multipartFile); + } + final Collection<DeltaReport> deltaReports = Collections.unmodifiableList( + cpsDeltaService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, sourceAnchorName, + xpath, yangResourceMap, jsonPayload.toString(), fetchDescendantsOption)); + return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaReports), 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 e4cd8c4be6..ba5104acf9 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 @@ -4,7 +4,7 @@ * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2022 Deutsche Telekom AG - * Modifications Copyright (C) 2022-2024 TechMahindra Ltd. + * Modifications Copyright (C) 2022-2025 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,6 @@ package org.onap.cps.rest.controller import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsFacade -import org.onap.cps.impl.DeltaReportBuilder import org.onap.cps.utils.ContentType import org.onap.cps.utils.DateTimeUtility import org.onap.cps.utils.JsonObjectMapper @@ -37,7 +36,6 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.http.HttpStatus import org.springframework.http.MediaType -import org.springframework.mock.web.MockMultipartFile import org.springframework.test.web.servlet.MockMvc import spock.lang.Shared import spock.lang.Specification @@ -46,7 +44,6 @@ import static org.onap.cps.api.parameters.FetchDescendantsOption.INCLUDE_ALL_DES import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put @@ -87,9 +84,6 @@ class DataRestControllerSpec extends Specification { @Shared def expectedXmlData = '<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<bookstore xmlns="org:onap:ccsdk:sample">\n</bookstore>' - @Shared - def multipartYangFile = new MockMultipartFile("file", 'filename.yang', "text/plain", 'content'.getBytes()) - def setup() { dataNodeBaseEndpointV1 = "$basePath/v1/dataspaces/$dataspaceName" dataNodeBaseEndpointV2 = "$basePath/v2/dataspaces/$dataspaceName" @@ -308,65 +302,6 @@ class DataRestControllerSpec extends Specification { 'JSON' | MediaType.APPLICATION_JSON || '[{"mocked":"result1"},{"mocked":"result2"}]' } - def 'Get delta between two anchors.'() { - given: 'the service returns a list containing delta reports' - def deltaReports = new DeltaReportBuilder().actionReplace().withXpath('some xpath').withSourceData('some key': 'some value').withTargetData('some key': 'some value').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\":\"replace\",\"xpath\":\"some xpath\",\"sourceData\":{\"some key\":\"some value\"},\"targetData\":{\"some key\":\"some value\"}}]") - } - - def 'Get delta between anchor and JSON payload with multipart file'() { - given: 'sample delta report, xpath, yang model file and json payload' - def deltaReports = new DeltaReportBuilder().actionCreate().withXpath('some xpath').build() - def xpath = 'some xpath' - def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/delta" - and: 'the service layer returns a list containing delta reports' - mockCpsDataService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, ['filename.yang':'content'], expectedJsonData, INCLUDE_ALL_DESCENDANTS) >> [deltaReports] - when: 'get delta request is performed using REST API' - def response = - mvc.perform(multipart(endpoint) - .file(multipartYangFile) - .param("json", requestBodyJson) - .param('xpath', xpath) - .contentType(MediaType.MULTIPART_FORM_DATA)) - .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\":\"create\",\"xpath\":\"some xpath\"}]") - } - - def 'Get delta between anchor and JSON payload without multipart file.'() { - given: 'sample delta report, xpath, and json payload' - def deltaReports = new DeltaReportBuilder().actionRemove().withXpath('some xpath').build() - def xpath = 'some xpath' - def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/delta" - and: 'the service layer returns a list containing delta reports' - mockCpsDataService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, [:], expectedJsonData, INCLUDE_ALL_DESCENDANTS) >> [deltaReports] - when: 'get delta request is performed using REST API' - def response = - mvc.perform(multipart(endpoint) - .param("json", requestBodyJson) - .param('xpath', xpath) - .contentType(MediaType.MULTIPART_FORM_DATA)) - .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\":\"remove\",\"xpath\":\"some xpath\"}]") - } - def 'Update data node leaves: #scenario.'() { given: 'endpoint to update a node ' def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes" diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DeltaRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DeltaRestControllerSpec.groovy new file mode 100644 index 0000000000..18c0f1369e --- /dev/null +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DeltaRestControllerSpec.groovy @@ -0,0 +1,129 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2025 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.rest.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.api.CpsDeltaService +import org.onap.cps.impl.DeltaReportBuilder +import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.mock.web.MockMultipartFile +import org.springframework.test.web.servlet.MockMvc +import org.springframework.web.multipart.MultipartFile +import spock.lang.Shared +import spock.lang.Specification + +import static org.onap.cps.api.parameters.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart + +@WebMvcTest(DeltaRestController) +class DeltaRestControllerSpec extends Specification { + + @SpringBean + CpsDeltaService mockCpsDeltaService = Mock() + + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + + @Autowired + MockMvc mvc + + @Value('${rest.api.cps-base-path}') + def basePath + + def dataNodeBaseEndpointV2 + def dataspaceName = 'my_dataspace' + def anchorName = 'my_anchor' + + @Shared + def requestBodyJson = '{"some-key":"some-value","categories":[{"books":[{"authors":["Iain M. Banks"]}]}]}' + @Shared + def expectedJsonData = '{"some-key":"some-value","categories":[{"books":[{"authors":["Iain M. Banks"]}]}]}' + @Shared + static MultipartFile multipartYangFile = new MockMultipartFile('file', 'filename.yang', 'text/plain', 'content'.getBytes()) + + def setup() { + dataNodeBaseEndpointV2 = "$basePath/v2/dataspaces/$dataspaceName/anchors/$anchorName/delta" + } + + def 'Get delta between two anchors'() { + given: 'the service returns a list containing delta reports' + def deltaReports = new DeltaReportBuilder().actionReplace().withXpath('some xpath').withSourceData('some key': 'some value').withTargetData('some key': 'some value').build() + def xpath = 'some xpath' + mockCpsDeltaService.getDeltaByDataspaceAndAnchors(dataspaceName, anchorName, 'targetAnchor', xpath, OMIT_DESCENDANTS) >> [deltaReports] + when: 'get delta request is performed using REST API' + def response = + mvc.perform(get(dataNodeBaseEndpointV2) + .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\":\"replace\",\"xpath\":\"some xpath\",\"sourceData\":{\"some key\":\"some value\"},\"targetData\":{\"some key\":\"some value\"}}]') + } + + def 'Get delta between anchor and JSON payload with multipart file'() { + given: 'sample delta report, xpath, yang model file and json payload' + def deltaReports = new DeltaReportBuilder().actionCreate().withXpath('some xpath').build() + def xpath = 'some xpath' + and: 'the service layer returns a list containing delta reports' + mockCpsDeltaService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, ['filename.yang':'content'], expectedJsonData, INCLUDE_ALL_DESCENDANTS) >> [deltaReports] + when: 'get delta request is performed using REST API' + def response = + mvc.perform(multipart(dataNodeBaseEndpointV2) + .file(multipartYangFile) + .param('json', requestBodyJson) + .param('xpath', xpath) + .contentType(MediaType.MULTIPART_FORM_DATA)) + .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\":\"create\",\"xpath\":\"some xpath\"}]') + } + + def 'Get delta between anchor and JSON payload without multipart file'() { + given: 'sample delta report, xpath, and json payload' + def deltaReports = new DeltaReportBuilder().actionRemove().withXpath('some xpath').build() + def xpath = 'some xpath' + and: 'the service layer returns a list containing delta reports' + mockCpsDeltaService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, [:], expectedJsonData, INCLUDE_ALL_DESCENDANTS) >> [deltaReports] + when: 'get delta request is performed using REST API' + def response = + mvc.perform(multipart(dataNodeBaseEndpointV2) + .param('json', requestBodyJson) + .param('xpath', xpath) + .contentType(MediaType.MULTIPART_FORM_DATA)) + .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\":\"remove\",\"xpath\":\"some xpath\"}]') + } +} diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy index 0cbdffbdc4..1d58197dcc 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy @@ -29,6 +29,7 @@ import groovy.json.JsonSlurper import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsDataspaceService +import org.onap.cps.api.CpsDeltaService import org.onap.cps.api.CpsFacade import org.onap.cps.api.CpsModuleService import org.onap.cps.api.CpsNotificationService @@ -95,6 +96,9 @@ class CpsRestExceptionHandlerSpec extends Specification { @SpringBean CpsNotificationService mockCpsNotificationService = Stub() + @SpringBean + CpsDeltaService cpsDeltaService = Stub() + @Autowired MockMvc mvc |