2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2022 Nordix Foundation
4 * Modifications Copyright (C) 2021-2022 Bell Canada.
5 * Modifications Copyright (C) 2021 Pantheon.tech
6 * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.cps.rest.controller
26 import org.onap.cps.spi.PaginationOption
27 import org.onap.cps.utils.PrefixResolver
29 import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
30 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
31 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
32 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
34 import com.fasterxml.jackson.databind.ObjectMapper
35 import org.onap.cps.utils.JsonObjectMapper
36 import org.onap.cps.api.CpsQueryService
37 import org.onap.cps.spi.model.DataNodeBuilder
38 import org.spockframework.spring.SpringBean
39 import org.springframework.beans.factory.annotation.Autowired
40 import org.springframework.beans.factory.annotation.Value
41 import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
42 import org.springframework.http.HttpStatus
43 import org.springframework.test.web.servlet.MockMvc
44 import spock.lang.Specification
46 @WebMvcTest(QueryRestController)
47 class QueryRestControllerSpec extends Specification {
50 CpsQueryService mockCpsQueryService = Mock()
53 JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
56 PrefixResolver prefixResolver = Mock()
61 @Value('${rest.api.cps-base-path}')
64 def dataspaceName = 'my_dataspace'
65 def anchorName = 'my_anchor'
66 def cpsPath = 'some cps-path'
67 def dataNodeEndpointV2
70 dataNodeEndpointV2 = "$basePath/v2/dataspaces/$dataspaceName/anchors/$anchorName/nodes/query"
73 def 'Query data node by cps path for the given dataspace and anchor with #scenario.'() {
74 given: 'service method returns a list containing a data node'
75 def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
76 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
77 mockCpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, expectedCpsDataServiceOption) >> [dataNode1, dataNode1]
78 and: 'the query endpoint'
79 def dataNodeEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName/nodes/query"
80 when: 'query data nodes API is invoked'
84 .param('cps-path', cpsPath)
85 .param('include-descendants', includeDescendantsOption))
87 then: 'the response contains the the datanode in json format'
88 response.status == HttpStatus.OK.value()
89 response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
90 where: 'the following options for include descendants are provided in the request'
91 scenario | includeDescendantsOption || expectedCpsDataServiceOption
92 'no descendants by default' | '' || OMIT_DESCENDANTS
93 'no descendant explicitly' | 'false' || OMIT_DESCENDANTS
94 'descendants' | 'true' || INCLUDE_ALL_DESCENDANTS
97 def 'Query data node v2 api by cps path for the given dataspace and anchor with #scenario.'() {
98 given: 'service method returns a list containing a data node'
99 def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
100 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
101 mockCpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, { descendantsOption -> {
102 assert descendantsOption.depth == expectedDepth}}) >> [dataNode1, dataNode1]
103 when: 'query data nodes API is invoked'
106 get(dataNodeEndpointV2)
107 .param('cps-path', cpsPath)
108 .param('descendants', includeDescendantsOptionString))
109 .andReturn().response
110 then: 'the response contains the the datanode in json format'
111 assert response.status == HttpStatus.OK.value()
112 assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
113 where: 'the following options for include descendants are provided in the request'
114 scenario | includeDescendantsOptionString || expectedDepth
115 'direct children' | 'direct' || 1
116 'descendants' | '2' || 2
119 def 'Query data node by cps path for the given dataspace across all anchors with #scenario.'() {
120 given: 'service method returns a list containing a data node from different anchors'
121 def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
122 .withAnchor('my_anchor')
123 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
124 def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
125 .withAnchor('my_anchor_2')
126 .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
127 and: 'second data node for the same anchor'
128 def dataNode3 = new DataNodeBuilder().withXpath('/xpath')
129 .withAnchor('my_anchor_2')
130 .withLeaves([leaf: 'value', leafList: ['leaveListElement5', 'leaveListElement6']]).build()
131 and: 'the query endpoint'
132 def dataspaceName = 'my_dataspace'
133 def cpsPath = 'some/cps/path'
134 def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
135 mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
136 expectedCpsDataServiceOption, PaginationOption.NO_PAGINATION) >> [dataNode1, dataNode2, dataNode3]
137 mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> 2
138 when: 'query data nodes API is invoked'
141 get(dataNodeEndpoint)
142 .param('cps-path', cpsPath)
143 .param('descendants', includeDescendantsOptionString))
144 .andReturn().response
145 then: 'the response contains the the datanode in json format'
146 response.status == HttpStatus.OK.value()
147 response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
148 response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
149 response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement5","leaveListElement6"]}}')
150 where: 'the following options for include descendants are provided in the request'
151 scenario | includeDescendantsOptionString || expectedCpsDataServiceOption
152 'no descendants by default' | '' || OMIT_DESCENDANTS
153 'no descendant explicitly' | 'none' || OMIT_DESCENDANTS
154 'descendants' | 'all' || INCLUDE_ALL_DESCENDANTS
155 'direct children' | 'direct' || DIRECT_CHILDREN_ONLY
158 def 'Query data node by cps path for the given dataspace across all anchors with pagination #scenario.'() {
159 given: 'service method returns a list containing a data node from different anchors'
160 def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
161 .withAnchor('my_anchor')
162 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
163 def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
164 .withAnchor('my_anchor_2')
165 .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
166 and: 'the query endpoint'
167 def dataspaceName = 'my_dataspace'
168 def cpsPath = 'some/cps/path'
169 def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
170 mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
171 INCLUDE_ALL_DESCENDANTS, new PaginationOption(pageIndex,pageSize)) >> [dataNode1, dataNode2]
172 mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> totalAnchors
173 when: 'query data nodes API is invoked'
176 get(dataNodeEndpoint)
177 .param('cps-path', cpsPath)
178 .param('descendants', "all")
179 .param('pageIndex', String.valueOf(pageIndex))
180 .param('pageSize', String.valueOf(pageSize)))
181 .andReturn().response
182 then: 'the response contains the the datanode in json format'
183 assert response.status == HttpStatus.OK.value()
184 assert Integer.valueOf(response.getHeaderValue("total-pages")) == expectedTotalPageSize
185 assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
186 assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
187 where: 'the following options for include descendants are provided in the request'
188 scenario | pageIndex | pageSize | totalAnchors || expectedTotalPageSize
189 '1st page with all anchors' | 1 | 3 | 3 || 1
190 '1st page with less anchors' | 1 | 2 | 3 || 2
193 def 'Query data node across all anchors with pagination option with #scenario.'() {
194 given: 'service method returns a list containing a data node from different anchors'
195 def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
196 .withAnchor('my_anchor')
197 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
198 def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
199 .withAnchor('my_anchor_2')
200 .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
201 and: 'the query endpoint'
202 def dataspaceName = 'my_dataspace'
203 def cpsPath = 'some/cps/path'
204 def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
205 mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
206 INCLUDE_ALL_DESCENDANTS, PaginationOption.NO_PAGINATION) >> [dataNode1, dataNode2]
207 mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> 2
208 when: 'query data nodes API is invoked'
211 get(dataNodeEndpoint)
212 .param('cps-path', cpsPath)
213 .param('descendants', "all")
214 .param(parameterName, "1"))
215 .andReturn().response
216 then: 'the response contains the the datanode in json format'
217 assert response.status == HttpStatus.OK.value()
218 assert Integer.valueOf(response.getHeaderValue("total-pages")) == 1
219 assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
220 assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
222 scenario | parameterName
223 'only page size' | 'pageSize'
224 'only page index' | 'pageIndex'