diff options
20 files changed, 222 insertions, 37 deletions
diff --git a/cps-rest/docs/openapi/cpsData.yml b/cps-rest/docs/openapi/cpsData.yml index daf59bbfbf..36000fd7d8 100644 --- a/cps-rest/docs/openapi/cpsData.yml +++ b/cps-rest/docs/openapi/cpsData.yml @@ -71,15 +71,24 @@ listElementByDataspaceAndAnchor: - $ref: 'components.yml#/components/parameters/anchorNameInPath' - $ref: 'components.yml#/components/parameters/requiredXpathInQuery' - $ref: 'components.yml#/components/parameters/observedTimestampInQuery' + - $ref: 'components.yml#/components/parameters/contentTypeInHeader' requestBody: required: true content: application/json: schema: - type: object + type: string examples: dataSample: $ref: 'components.yml#/components/examples/dataSample' + application/xml: + schema: + type: object + xml: + name: stores + examples: + dataSample: + $ref: 'components.yml#/components/examples/dataSampleXml' responses: '200': $ref: 'components.yml#/components/responses/Ok' 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 dda88e019c..3efb6b421c 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 @@ -173,9 +173,11 @@ public class DataRestController implements CpsDataApi { @Override public ResponseEntity<Object> replaceListContent(final String apiVersion, final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Object jsonData, final String observedTimestamp) { + final String nodeData, final String observedTimestamp, + final String contentTypeInHeader) { + final ContentType contentType = ContentType.fromString(contentTypeInHeader); cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, - jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp)); + nodeData, toOffsetDateTime(observedTimestamp), contentType); return new ResponseEntity<>(HttpStatus.OK); } @@ -225,10 +227,10 @@ public class DataRestController implements CpsDataApi { return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), HttpStatus.OK); } - ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataMaps, + private ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataMaps, final ContentType contentType) { final String responseData; - if (contentType == ContentType.XML) { + if (ContentType.XML.equals(contentType)) { responseData = XmlFileUtils.convertDataMapsToXml(dataMaps); } else { responseData = jsonObjectMapper.asJsonString(dataMaps); diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java index 55a1886ce7..ec71c30a75 100644 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java @@ -151,7 +151,7 @@ public class QueryRestController implements CpsQueryApi { private ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataNodesAsListOfMaps, final ContentType contentType) { final String responseData; - if (contentType == ContentType.XML) { + if (ContentType.XML.equals(contentType)) { responseData = XmlFileUtils.convertDataMapsToXml(dataNodesAsListOfMaps); } else { responseData = jsonObjectMapper.asJsonString(dataNodesAsListOfMaps); 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 72ae4c7f91..892963c827 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 @@ -576,7 +576,28 @@ class DataRestControllerSpec extends Specification { response.status == expectedHttpStatus.value() and: 'the java API was called with the correct parameters' expectedApiCount * mockCpsDataService.replaceListContent(dataspaceName, anchorName, 'parent xpath', expectedJsonData, - { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }) + { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, ContentType.JSON) + where: + scenario | observedTimestamp || expectedApiCount | expectedHttpStatus + 'with observed-timestamp' | '2021-03-03T23:59:59.999-0400' || 1 | HttpStatus.OK + 'without observed-timestamp' | null || 1 | HttpStatus.OK + 'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST + } + + def 'Replace list XML content #scenario.'() { + when: 'list-nodes endpoint is invoked with put (update) operation' + def putRequestBuilder = put("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes") + .contentType(MediaType.APPLICATION_XML) + .param('xpath', 'parent xpath') + .content(requestBodyXml) + if (observedTimestamp != null) + putRequestBuilder.param('observed-timestamp', observedTimestamp) + def response = mvc.perform(putRequestBuilder).andReturn().response + then: 'a success response is returned' + response.status == expectedHttpStatus.value() + and: 'the java API was called with the correct parameters' + expectedApiCount * mockCpsDataService.replaceListContent(dataspaceName, anchorName, 'parent xpath', expectedXmlData, + { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, ContentType.XML) where: scenario | observedTimestamp || expectedApiCount | expectedHttpStatus 'with observed-timestamp' | '2021-03-03T23:59:59.999-0400' || 1 | HttpStatus.OK diff --git a/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java index 3368aee148..e5853abf39 100755 --- a/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java @@ -230,8 +230,6 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ updateAllModuleReferences(allModuleReferences, schemaSetEntity.getId()); } - - @Override @Transactional public void deleteUnusedYangResourceModules() { diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java index b3eff8eb26..29c8ad0168 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java @@ -172,11 +172,12 @@ public interface CpsDataService { * @param dataspaceName dataspace name * @param anchorName anchor name * @param parentNodeXpath parent node xpath - * @param jsonData json data representing the new list elements + * @param nodeData node data representing the new list elements * @param observedTimestamp observedTimestamp + * @param contentType JSON/XML content type */ - void replaceListContent(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData, - OffsetDateTime observedTimestamp); + void replaceListContent(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, + OffsetDateTime observedTimestamp, ContentType contentType); /** * Replaces list content by removing all existing elements and inserting the given new elements as data nodes diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java index bbfe496d85..304ed288f5 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java @@ -188,4 +188,9 @@ public interface CpsModuleService { final Map<String, String> parentAttributes, final Map<String, String> childAttributes); + /** + * Remove any Yang Resource Modules from the DB that are no longer referenced by any schema set. + */ + void deleteUnusedYangResourceModules(); + } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java index b1b545be68..a63b3e5360 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java @@ -281,11 +281,11 @@ public class CpsDataServiceImpl implements CpsDataService { @Timed(value = "cps.data.service.list.update", description = "Time taken to update a list") public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final String jsonData, final OffsetDateTime observedTimestamp) { + final String nodeData, final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> newListElements = - buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON); + buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType); replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java index 4063a7f769..9f3f2cc571 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java @@ -106,7 +106,6 @@ public class CpsModuleServiceImpl implements CpsModuleService { } cpsModulePersistenceService.deleteSchemaSet(dataspaceName, schemaSetName); yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName); - cpsModulePersistenceService.deleteUnusedYangResourceModules(); } @Override @@ -119,7 +118,6 @@ public class CpsModuleServiceImpl implements CpsModuleService { .stream().map(Anchor::getName).collect(Collectors.toSet()); cpsAnchorService.deleteAnchors(dataspaceName, anchorNames); cpsModulePersistenceService.deleteSchemaSets(dataspaceName, schemaSetNames); - cpsModulePersistenceService.deleteUnusedYangResourceModules(); for (final String schemaSetName : schemaSetNames) { yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName); } @@ -182,6 +180,11 @@ public class CpsModuleServiceImpl implements CpsModuleService { childAttributes); } + @Override + public void deleteUnusedYangResourceModules() { + cpsModulePersistenceService.deleteUnusedYangResourceModules(); + } + private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) { return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed; } diff --git a/cps-service/src/main/java/org/onap/cps/init/DbCleaner.java b/cps-service/src/main/java/org/onap/cps/init/DbCleaner.java new file mode 100644 index 0000000000..6bd3e1f204 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/init/DbCleaner.java @@ -0,0 +1,48 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.init; + +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.api.CpsModuleService; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Slf4j +@RequiredArgsConstructor +@Service +public class DbCleaner { + + private final CpsModuleService cpsModuleService; + + /** + * This method will clean up the db during application start up. + * It wil run once and currently only removes unused yang resource modules. + * + */ + @Scheduled(initialDelay = 1, timeUnit = TimeUnit.SECONDS) + public void cleanDbAtStartUp() { + log.info("CPS Application started, commencing DB clean up"); + cpsModuleService.deleteUnusedYangResourceModules(); + log.info("DB clean up completed"); + } +} diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index 8c208a1cf8..1543fb931b 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -448,12 +448,12 @@ class CpsDataServiceImplSpec extends Specification { assert thrownUp == originalException } - def 'Replace list content data fragment under parent node.'() { + def 'Replace list content data fragment JSON under parent node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'replace list data method is invoked with list element json data' def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}' - objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) + objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp, ContentType.JSON) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree', { dataNodeCollection -> @@ -468,12 +468,42 @@ class CpsDataServiceImplSpec extends Specification { 2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) } + def 'Replace list content data fragment XML under parent node.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'replace list data method is invoked with list element xml data' + def nodeData = '<branch><name>A</name></branch>' + objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', nodeData, observedTimestamp, ContentType.XML) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree', + { dataNodeCollection -> + { + assert dataNodeCollection.size() == 1 + assert dataNodeCollection.collect { it.getXpath() } + .containsAll(['/test-tree/branch[@name=\'A\']']) + } + } + ) + and: 'the CpsValidator is called on the dataspaceName and AnchorName twice' + 2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + } + def 'Replace whole list content with empty list element.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'replace list data method is invoked with empty list' def jsonData = '{"branch": []}' - objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) + objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp, ContentType.JSON) + then: 'invalid data exception is thrown' + thrown(DataValidationException) + } + + def 'Replace whole list content XML with empty list element.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'replace list data method is invoked with xml empty list' + def nodeData = '[]' + objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', nodeData, observedTimestamp, ContentType.XML) then: 'invalid data exception is thrown' thrown(DataValidationException) } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy index 1831506563..c02b06fd80 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy @@ -142,8 +142,6 @@ class CpsModuleServiceImplSpec extends Specification { 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset') and: 'schema set will be removed from the cache' 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset') - and: 'orphan yang resources are deleted' - 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() and: 'the CpsValidator is called on the dataspaceName and schemaSetName' 1 * mockCpsValidator.validateNameCharacters('my-dataspace', _) where: 'following parameters are used' @@ -161,8 +159,6 @@ class CpsModuleServiceImplSpec extends Specification { 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset') and: 'schema set will be removed from the cache' 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset') - and: 'orphan yang resources are deleted' - 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() and: 'the CpsValidator is called on the dataspaceName and schemaSetName' 1 * mockCpsValidator.validateNameCharacters('my-dataspace', 'my-schemaset') } @@ -187,8 +183,6 @@ class CpsModuleServiceImplSpec extends Specification { mockCpsModulePersistenceService.deleteSchemaSets('my-dataspace', _) and: 'schema sets will be removed from the cache' 2 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', _) - and: 'orphan yang resources are deleted' - 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() and: 'the CpsValidator is called on the dataspaceName' 1 * mockCpsValidator.validateNameCharacters('my-dataspace') and: 'the CpsValidator is called on the schemaSetNames' @@ -276,6 +270,13 @@ class CpsModuleServiceImplSpec extends Specification { 1 * mockCpsValidator.validateNameCharacters('some-dataspace-name', 'some-anchor-name') } + def 'Delete unused yang resource modules.'() { + when: 'deleting unused yang resource modules' + objectUnderTest.deleteUnusedYangResourceModules() + then: 'it is delegated to the module persistence service' + 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() + } + def getModuleReferences() { return [new ModuleReference('some module name','some revision name')] } diff --git a/cps-service/src/test/groovy/org/onap/cps/init/DbCleanerSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/init/DbCleanerSpec.groovy new file mode 100644 index 0000000000..5106d29fa5 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/init/DbCleanerSpec.groovy @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.init + +import org.onap.cps.api.CpsModuleService +import spock.lang.Specification + +class DbCleanerSpec extends Specification { + + def mockCpsModuleService = Mock(CpsModuleService) + + def objectUnderTest = new DbCleaner(mockCpsModuleService) + + def 'DB clean up.'() { + when: 'scheduled method is triggered' + objectUnderTest.cleanDbAtStartUp() + then: 'the unused yang resource modules are deleted' + 1 * mockCpsModuleService.deleteUnusedYangResourceModules() + } +} diff --git a/docs/api/swagger/cps/openapi.yaml b/docs/api/swagger/cps/openapi.yaml index 812a2e4e60..fea68480ab 100644 --- a/docs/api/swagger/cps/openapi.yaml +++ b/docs/api/swagger/cps/openapi.yaml @@ -1927,6 +1927,16 @@ paths: schema: example: 2021-03-21T00:10:34.030-0100 type: string + - description: Content type in header + in: header + name: Content-Type + required: false + schema: + default: application/json + enum: + - application/json + - application/xml + type: string requestBody: content: application/json: @@ -1935,7 +1945,16 @@ paths: $ref: '#/components/examples/dataSample' value: null schema: + type: string + application/xml: + examples: + dataSample: + $ref: '#/components/examples/dataSampleXml' + value: null + schema: type: object + xml: + name: stores required: true responses: "200": diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy index d49931eb7e..6c68a37f81 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy @@ -310,9 +310,9 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase { def 'Attempt to add empty lists.'() { when: 'the batches of new list element(s) are saved' - objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [ ], now) - then: 'an admin exception is thrown' - thrown(CpsAdminException) + objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [ ] as String, now, ContentType.JSON) + then: 'an data exception is thrown' + thrown(DataValidationException) } def 'Add child error scenario: #scenario.'() { @@ -344,7 +344,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase { assert countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="2"]', DIRECT_CHILDREN_ONLY)) > 1 when: 'the categories list is replaced with just category "1" and without child nodes (books)' def json = '{"categories": [ {"code":"' +categoryCode + '"' + childJson + '} ] }' - objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now) + objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now, ContentType.JSON) then: 'the new replaced category can be retrieved but has no children anymore' assert expectedNumberOfDataNodes == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="' +categoryCode + '"]', DIRECT_CHILDREN_ONLY)) when: 'attempt to retrieve a category (code) not in the new list' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy index d69f6cca0c..f20e4e5cad 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy @@ -32,6 +32,8 @@ class DataspaceServiceIntegrationSpec extends FunctionalSpecBase { def setup() { objectUnderTest = cpsDataspaceService } + def cleanup() { cpsModuleService.deleteUnusedYangResourceModules() } + def 'Dataspace CRUD operations.'() { when: 'a dataspace is created' objectUnderTest.createDataspace('newDataspace') diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy index 9e51d80d9e..a50a59a3d7 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy @@ -59,9 +59,9 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase { def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang') def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang') - def setup() { - objectUnderTest = cpsModuleService - } + def setup() { objectUnderTest = cpsModuleService } + + def cleanup() { objectUnderTest.deleteUnusedYangResourceModules() } /* C R E A T E S C H E M A S E T U S E - C A S E S @@ -77,7 +77,7 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase { originalNumberOfModuleReferences + numberOfNewModules == yangResourceModuleReferences.size() cleanup: objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchemaSet' ]) - where: 'the following parameters are use' + where: 'the following parameters are used' scenario | numberOfNewModules 'two valid new modules' | 2 'empty schema set' | 0 diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy index 20fa546eba..43bcbdb4f4 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy @@ -24,6 +24,7 @@ import io.micrometer.core.instrument.MeterRegistry import org.onap.cps.integration.base.CpsIntegrationSpecBase import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog import org.springframework.beans.factory.annotation.Autowired +import org.springframework.util.StopWatch import spock.util.concurrent.PollingConditions import java.util.concurrent.Executors @@ -61,12 +62,13 @@ class ModuleSyncWatchdogIntegrationSpec extends CpsIntegrationSpecBase { assert moduleSyncWorkQueue.isEmpty() } - def 'CPS-2478 Highlight module sync inefficiencies.'() { + def 'CPS-2478 Highlight (and improve) module sync inefficiencies.'() { given: 'register 250 cm handles with module set tag cps-2478-A' def numberOfTags = 2 def cmHandlesPerTag = 250 def totalCmHandles = numberOfTags * cmHandlesPerTag def offset = 1 + def minimumBatches = totalCmHandles / 100 registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, 'cps-2478-A', cmHandlesPerTag, offset) and: 'register anther 250 cm handles with module set tag cps-2478-B' offset += cmHandlesPerTag @@ -86,7 +88,7 @@ class ModuleSyncWatchdogIntegrationSpec extends CpsIntegrationSpecBase { then: 'wait till at least 5 batches of state updates are done (often more because of retries of locked cm handles)' def dbStateUpdateTimer = meterRegistry.get('cps.ncmp.cmhandle.state.update.batch').timer() new PollingConditions().within(10, () -> { - assert dbStateUpdateTimer.count() >= 5 + assert dbStateUpdateTimer.count() >= minimumBatches }) and: 'the db has been queried for tags exactly 2 times.' def dbModuleQueriesTimer = meterRegistry.get('cps.module.service.module.reference.query.by.attribute').timer() @@ -100,7 +102,12 @@ class ModuleSyncWatchdogIntegrationSpec extends CpsIntegrationSpecBase { logInstrumentation(dbSchemaSetStorageTimer, 'store schema sets ') logInstrumentation(dbStateUpdateTimer, 'batch state updates ') cleanup: 'remove all cm handles' + // To properly measure performance the sample-size should be increased to 20,000 cm handles or higher (10,000 per tag) + def stopWatch = new StopWatch() + stopWatch.start() deregisterSequenceOfCmHandles(DMI1_URL, totalCmHandles, 1) + stopWatch.stop() + println "*** CPS-2478, Deletion of $totalCmHandles cm handles took ${stopWatch.getTotalTimeMillis()} milliseconds" } def 'Populate module sync work queue simultaneously on two parallel threads (CPS-2403).'() { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/ModuleQueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/ModuleQueryPerfTest.groovy index add931a1ad..914f2030cc 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/ModuleQueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/ModuleQueryPerfTest.groovy @@ -100,6 +100,7 @@ class ModuleQueryPerfTest extends CpsPerfTestBase { cpsModuleService.deleteSchemaSetsWithCascade(CPS_PERFORMANCE_TEST_DATASPACE, (i..i+100).collect {SCHEMA_SET_PREFIX + it}) } cpsModuleService.deleteSchemaSetsWithCascade(CPS_PERFORMANCE_TEST_DATASPACE, [SCHEMA_SET_PREFIX + '0']) + cpsModuleService.deleteUnusedYangResourceModules() } // This makes a Yang module of approximately target length in bytes by padding the description field with many '*' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy index 03abdb4b3f..d764029f02 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy @@ -94,7 +94,7 @@ class UpdatePerfTest extends CpsPerfTestBase { def jsonListData = generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) when: 'the container node is updated' resourceMeter.start() - objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now) + objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now, ContentType.JSON) resourceMeter.stop() then: 'there are the expected number of total nodes' assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device') @@ -118,7 +118,7 @@ class UpdatePerfTest extends CpsPerfTestBase { def 'Update leaves for 100 data nodes.'() { given: 'there are 200 existing data nodes' def jsonListData = generateJsonForOpenRoadmDevices(1, 200, false) - objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now) + objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now, ContentType.JSON) and: 'JSON for updated data leaves of 100 nodes' def jsonDataUpdated = "{'openroadm-device':[" + (1..100).collect {"{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}" when: 'update is performed for leaves' |