diff options
Diffstat (limited to 'cps-service/src')
27 files changed, 324 insertions, 1331 deletions
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java b/cps-service/src/main/java/org/onap/cps/api/CpsAnchorService.java index edd052a51c..a247150c15 100755..100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsAnchorService.java @@ -1,9 +1,6 @@ /* - * ============LICENSE_START======================================================= - * Copyright (C) 2020-2023 Nordix Foundation - * Modifications Copyright (C) 2020-2022 Bell Canada. - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2022 TechMahindra Ltd. + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,46 +21,10 @@ package org.onap.cps.api; import java.util.Collection; -import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.spi.model.Anchor; -import org.onap.cps.spi.model.Dataspace; -/** - * CPS Admin Service. - */ -public interface CpsAdminService { - - /** - * Create dataspace. - * - * @param dataspaceName dataspace name - * @throws AlreadyDefinedException if dataspace with same name already exists - */ - void createDataspace(String dataspaceName); - - /** - * Delete dataspace. - * - * @param dataspaceName the name of the dataspace to delete - */ - void deleteDataspace(String dataspaceName); - - /** - * Get dataspace by given dataspace name. - * - * @param dataspaceName dataspace name - * @return a dataspace - */ - Dataspace getDataspace(String dataspaceName); - - /** - * Get All Dataspaces. - * - * - * @return a collection of dataspaces - */ - Collection<Dataspace> getAllDataspaces(); +public interface CpsAnchorService { /** * Create an Anchor. diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataspaceService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataspaceService.java new file mode 100644 index 0000000000..7b94604261 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataspaceService.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020-2023 Nordix Foundation + * Modifications Copyright (C) 2020-2022 Bell Canada. + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2022 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.api; + +import java.util.Collection; +import org.onap.cps.spi.exceptions.AlreadyDefinedException; +import org.onap.cps.spi.model.Dataspace; + +/** + * CPS Admin Service. + */ +public interface CpsDataspaceService { + + /** + * Create dataspace. + * + * @param dataspaceName dataspace name + * @throws AlreadyDefinedException if dataspace with same name already exists + */ + void createDataspace(String dataspaceName); + + /** + * Delete dataspace. + * + * @param dataspaceName the name of the dataspace to delete + */ + void deleteDataspace(String dataspaceName); + + /** + * Get dataspace by given dataspace name. + * + * @param dataspaceName dataspace name + * @return a dataspace + */ + Dataspace getDataspace(String dataspaceName); + + /** + * Get All Dataspaces. + * + * + * @return a collection of dataspaces + */ + Collection<Dataspace> getAllDataspaces(); + +} diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java index d83ee434de..f09a795a66 100755..100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java @@ -1,9 +1,6 @@ /* - * ============LICENSE_START======================================================= - * Copyright (C) 2020-2023 Nordix Foundation - * Modifications Copyright (C) 2020-2022 Bell Canada. - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2022 TechMahindra Ltd. + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,52 +20,25 @@ package org.onap.cps.api.impl; -import java.time.OffsetDateTime; import java.util.Collection; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.onap.cps.api.CpsAdminService; -import org.onap.cps.api.CpsDataService; +import org.onap.cps.api.CpsAnchorService; import org.onap.cps.spi.CpsAdminPersistenceService; +import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.model.Anchor; -import org.onap.cps.spi.model.Dataspace; import org.onap.cps.spi.utils.CpsValidator; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@Component("CpsAdminServiceImpl") -@RequiredArgsConstructor(onConstructor = @__(@Lazy)) -public class CpsAdminServiceImpl implements CpsAdminService { +@Service +@RequiredArgsConstructor +public class CpsAnchorServiceImpl implements CpsAnchorService { private final CpsAdminPersistenceService cpsAdminPersistenceService; - @Lazy - private final CpsDataService cpsDataService; + private final CpsDataPersistenceService cpsDataPersistenceService; private final CpsValidator cpsValidator; @Override - public void createDataspace(final String dataspaceName) { - cpsValidator.validateNameCharacters(dataspaceName); - cpsAdminPersistenceService.createDataspace(dataspaceName); - } - - @Override - public void deleteDataspace(final String dataspaceName) { - cpsValidator.validateNameCharacters(dataspaceName); - cpsAdminPersistenceService.deleteDataspace(dataspaceName); - } - - @Override - public Dataspace getDataspace(final String dataspaceName) { - cpsValidator.validateNameCharacters(dataspaceName); - return cpsAdminPersistenceService.getDataspace(dataspaceName); - } - - @Override - public Collection<Dataspace> getAllDataspaces() { - return cpsAdminPersistenceService.getAllDataspaces(); - } - - @Override public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) { cpsValidator.validateNameCharacters(dataspaceName, schemaSetName, anchorName); cpsAdminPersistenceService.createAnchor(dataspaceName, schemaSetName, anchorName); @@ -102,7 +72,7 @@ public class CpsAdminServiceImpl implements CpsAdminService { @Override public void deleteAnchor(final String dataspaceName, final String anchorName) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - cpsDataService.deleteDataNodes(dataspaceName, anchorName, OffsetDateTime.now()); + cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName); cpsAdminPersistenceService.deleteAnchor(dataspaceName, anchorName); } @@ -110,7 +80,7 @@ public class CpsAdminServiceImpl implements CpsAdminService { public void deleteAnchors(final String dataspaceName, final Collection<String> anchorNames) { cpsValidator.validateNameCharacters(dataspaceName); cpsValidator.validateNameCharacters(anchorNames); - cpsDataService.deleteDataNodes(dataspaceName, anchorNames, OffsetDateTime.now()); + cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorNames); cpsAdminPersistenceService.deleteAnchors(dataspaceName, anchorNames); } @@ -123,8 +93,8 @@ public class CpsAdminServiceImpl implements CpsAdminService { @Override public void updateAnchorSchemaSet(final String dataspaceName, - final String anchorName, - final String schemaSetName) { + final String anchorName, + final String schemaSetName) { cpsAdminPersistenceService.updateAnchorSchemaSet(dataspaceName, anchorName, schemaSetName); } } 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 e74e0ad249..a1bae6a441 100755 --- 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 @@ -24,10 +24,6 @@ package org.onap.cps.api.impl; -import static org.onap.cps.notification.Operation.CREATE; -import static org.onap.cps.notification.Operation.DELETE; -import static org.onap.cps.notification.Operation.UPDATE; - import io.micrometer.core.annotation.Timed; import java.io.Serializable; import java.time.OffsetDateTime; @@ -39,12 +35,10 @@ import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.api.CpsAdminService; +import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsDeltaService; import org.onap.cps.cpspath.parser.CpsPathUtil; -import org.onap.cps.notification.NotificationService; -import org.onap.cps.notification.Operation; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.DataValidationException; @@ -68,9 +62,8 @@ public class CpsDataServiceImpl implements CpsDataService { private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L; private final CpsDataPersistenceService cpsDataPersistenceService; - private final CpsAdminService cpsAdminService; + private final CpsAnchorService cpsAnchorService; private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; - private final NotificationService notificationService; private final CpsValidator cpsValidator; private final TimedYangParser timedYangParser; private final CpsDeltaService cpsDeltaService; @@ -87,10 +80,9 @@ public class CpsDataServiceImpl implements CpsDataService { public void saveData(final String dataspaceName, final String anchorName, final String nodeData, final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodes = buildDataNodes(anchor, ROOT_NODE_XPATH, nodeData, contentType); cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, dataNodes); - processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, CREATE, observedTimestamp); } @Override @@ -106,10 +98,9 @@ public class CpsDataServiceImpl implements CpsDataService { final String nodeData, final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodes = buildDataNodes(anchor, parentNodeXpath, nodeData, contentType); cpsDataPersistenceService.addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes); - processDataUpdatedEventAsync(anchor, parentNodeXpath, CREATE, observedTimestamp); } @Override @@ -118,7 +109,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void saveListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> listElementDataNodeCollection = buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON); if (isRootNodeXpath(parentNodeXpath)) { @@ -127,7 +118,6 @@ public class CpsDataServiceImpl implements CpsDataService { cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath, listElementDataNodeCollection); } - processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp); } @Override @@ -136,12 +126,11 @@ public class CpsDataServiceImpl implements CpsDataService { public void saveListElementsBatch(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<String> jsonDataList, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<Collection<DataNode>> listElementDataNodeCollections = buildDataNodes(anchor, parentNodeXpath, jsonDataList, ContentType.JSON); cpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, parentNodeXpath, listElementDataNodeCollections); - processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp); } @Override @@ -171,13 +160,12 @@ public class CpsDataServiceImpl implements CpsDataService { public void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodesInPatch = buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON); final Map<String, Map<String, Serializable>> xpathToUpdatedLeaves = dataNodesInPatch.stream() .collect(Collectors.toMap(DataNode::getXpath, DataNode::getLeaves)); cpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, xpathToUpdatedLeaves); - processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp); } @Override @@ -188,13 +176,12 @@ public class CpsDataServiceImpl implements CpsDataService { final String dataNodeUpdatesAsJson, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodeUpdates = buildDataNodes(anchor, parentNodeXpath, dataNodeUpdatesAsJson, ContentType.JSON); for (final DataNode dataNodeUpdate : dataNodeUpdates) { processDataNodeUpdate(anchor, dataNodeUpdate); } - processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp); } @Override @@ -241,10 +228,9 @@ public class CpsDataServiceImpl implements CpsDataService { final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodes = buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); - processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp); } @Override @@ -254,11 +240,9 @@ public class CpsDataServiceImpl implements CpsDataService { final Map<String, String> nodesJsonData, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodes = buildDataNodes(anchor, nodesJsonData); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); - nodesJsonData.keySet().forEach(nodeXpath -> - processDataUpdatedEventAsync(anchor, nodeXpath, UPDATE, observedTimestamp)); } @Override @@ -267,7 +251,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> newListElements = buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON); replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp); @@ -279,9 +263,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes); - processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp); } @Override @@ -290,9 +272,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath); - processDataUpdatedEventAsync(anchor, dataNodeXpath, DELETE, observedTimestamp); } @Override @@ -302,9 +282,6 @@ public class CpsDataServiceImpl implements CpsDataService { final Collection<String> dataNodeXpaths, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName, dataNodeXpaths); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); - dataNodeXpaths.forEach(dataNodeXpath -> - processDataUpdatedEventAsync(anchor, dataNodeXpath, DELETE, observedTimestamp)); } @Override @@ -313,8 +290,6 @@ public class CpsDataServiceImpl implements CpsDataService { public void deleteDataNodes(final String dataspaceName, final String anchorName, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); - processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, DELETE, observedTimestamp); cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName); } @@ -325,9 +300,6 @@ public class CpsDataServiceImpl implements CpsDataService { final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName); cpsValidator.validateNameCharacters(anchorNames); - for (final Anchor anchor : cpsAdminService.getAnchors(dataspaceName, anchorNames)) { - processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, DELETE, observedTimestamp); - } cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorNames); } @@ -337,9 +309,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath, final OffsetDateTime observedTimestamp) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath); - processDataUpdatedEventAsync(anchor, listNodeXpath, DELETE, observedTimestamp); } private Collection<DataNode> buildDataNodes(final Anchor anchor, final Map<String, String> nodesJsonData) { @@ -385,16 +355,6 @@ public class CpsDataServiceImpl implements CpsDataService { .collect(Collectors.toList()); } - private void processDataUpdatedEventAsync(final Anchor anchor, final String xpath, - final Operation operation, final OffsetDateTime observedTimestamp) { - try { - notificationService.processDataUpdatedEvent(anchor, xpath, operation, observedTimestamp); - } catch (final Exception exception) { - //If async message can't be queued for notification service, the initial request should not fail. - log.error("Failed to send message to notification service", exception); - } - } - private SchemaContext getSchemaContext(final Anchor anchor) { return yangTextSchemaSourceSetCache .get(anchor.getDataspaceName(), anchor.getSchemaSetName()).getSchemaContext(); diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java new file mode 100644 index 0000000000..a7f5da4874 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020-2023 Nordix Foundation + * Modifications Copyright (C) 2020-2022 Bell Canada. + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2022 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.api.impl; + +import java.util.Collection; +import lombok.RequiredArgsConstructor; +import org.onap.cps.api.CpsDataspaceService; +import org.onap.cps.spi.CpsAdminPersistenceService; +import org.onap.cps.spi.model.Dataspace; +import org.onap.cps.spi.utils.CpsValidator; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CpsDataspaceServiceImpl implements CpsDataspaceService { + + private final CpsAdminPersistenceService cpsAdminPersistenceService; + private final CpsValidator cpsValidator; + + @Override + public void createDataspace(final String dataspaceName) { + cpsValidator.validateNameCharacters(dataspaceName); + cpsAdminPersistenceService.createDataspace(dataspaceName); + } + + @Override + public void deleteDataspace(final String dataspaceName) { + cpsValidator.validateNameCharacters(dataspaceName); + cpsAdminPersistenceService.deleteDataspace(dataspaceName); + } + + @Override + public Dataspace getDataspace(final String dataspaceName) { + cpsValidator.validateNameCharacters(dataspaceName); + return cpsAdminPersistenceService.getDataspace(dataspaceName); + } + + @Override + public Collection<Dataspace> getAllDataspaces() { + return cpsAdminPersistenceService.getAllDataspaces(); + } + +} 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 5337237846..61a4e623af 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 @@ -28,7 +28,7 @@ import java.util.Collection; import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.onap.cps.api.CpsAdminService; +import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsModuleService; import org.onap.cps.spi.CascadeDeleteAllowed; import org.onap.cps.spi.CpsModulePersistenceService; @@ -49,7 +49,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { private final CpsModulePersistenceService cpsModulePersistenceService; private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; - private final CpsAdminService cpsAdminService; + private final CpsAnchorService cpsAnchorService; private final CpsValidator cpsValidator; private final TimedYangTextSchemaSourceSetBuilder timedYangTextSchemaSourceSetBuilder; @@ -97,12 +97,12 @@ public class CpsModuleServiceImpl implements CpsModuleService { public void deleteSchemaSet(final String dataspaceName, final String schemaSetName, final CascadeDeleteAllowed cascadeDeleteAllowed) { cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); - final Collection<Anchor> anchors = cpsAdminService.getAnchors(dataspaceName, schemaSetName); + final Collection<Anchor> anchors = cpsAnchorService.getAnchors(dataspaceName, schemaSetName); if (!anchors.isEmpty() && isCascadeDeleteProhibited(cascadeDeleteAllowed)) { throw new SchemaSetInUseException(dataspaceName, schemaSetName); } for (final Anchor anchor : anchors) { - cpsAdminService.deleteAnchor(dataspaceName, anchor.getName()); + cpsAnchorService.deleteAnchor(dataspaceName, anchor.getName()); } cpsModulePersistenceService.deleteSchemaSet(dataspaceName, schemaSetName); yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName); @@ -114,9 +114,9 @@ public class CpsModuleServiceImpl implements CpsModuleService { public void deleteSchemaSetsWithCascade(final String dataspaceName, final Collection<String> schemaSetNames) { cpsValidator.validateNameCharacters(dataspaceName); cpsValidator.validateNameCharacters(schemaSetNames); - final Collection<String> anchorNames = cpsAdminService.getAnchors(dataspaceName, schemaSetNames) + final Collection<String> anchorNames = cpsAnchorService.getAnchors(dataspaceName, schemaSetNames) .stream().map(Anchor::getName).collect(Collectors.toSet()); - cpsAdminService.deleteAnchors(dataspaceName, anchorNames); + cpsAnchorService.deleteAnchors(dataspaceName, anchorNames); cpsModulePersistenceService.deleteUnusedYangResourceModules(); cpsModulePersistenceService.deleteSchemaSets(dataspaceName, schemaSetNames); for (final String schemaSetName : schemaSetNames) { diff --git a/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java b/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java deleted file mode 100644 index 696fd60f8c..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2021-2022 Bell Canada. - * Modifications Copyright (c) 2022-2023 Nordix Foundation - * Modifications Copyright (C) 2023 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.notification; - -import java.net.URI; -import java.net.URISyntaxException; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.util.UUID; -import lombok.AllArgsConstructor; -import lombok.SneakyThrows; -import org.onap.cps.api.CpsDataService; -import org.onap.cps.event.model.Content; -import org.onap.cps.event.model.CpsDataUpdatedEvent; -import org.onap.cps.event.model.Data; -import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.model.Anchor; -import org.onap.cps.spi.model.DataNode; -import org.onap.cps.utils.DataMapUtils; -import org.onap.cps.utils.PrefixResolver; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -@Component -@AllArgsConstructor(onConstructor = @__(@Lazy)) -public class CpsDataUpdatedEventFactory { - - private static final DateTimeFormatter DATE_TIME_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - - @Lazy - private final CpsDataService cpsDataService; - - @Lazy - private final PrefixResolver prefixResolver; - - /** - * Generates CPS Data Updated event. If observedTimestamp is not provided, then current timestamp is used. - * - * @param anchor anchor - * @param observedTimestamp observedTimestamp - * @param operation operation - * @return CpsDataUpdatedEvent - */ - public CpsDataUpdatedEvent createCpsDataUpdatedEvent(final Anchor anchor, - final OffsetDateTime observedTimestamp, final Operation operation) { - final var dataNode = (operation == Operation.DELETE) ? null : - cpsDataService.getDataNodes(anchor.getDataspaceName(), anchor.getName(), - "/", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS).iterator().next(); - return toCpsDataUpdatedEvent(anchor, dataNode, observedTimestamp, operation); - } - - @SneakyThrows(URISyntaxException.class) - private CpsDataUpdatedEvent toCpsDataUpdatedEvent(final Anchor anchor, - final DataNode dataNode, - final OffsetDateTime observedTimestamp, - final Operation operation) { - final CpsDataUpdatedEvent cpsDataUpdatedEvent = new CpsDataUpdatedEvent(); - cpsDataUpdatedEvent.withContent(createContent(anchor, dataNode, observedTimestamp, operation)); - cpsDataUpdatedEvent.withId(UUID.randomUUID().toString()); - cpsDataUpdatedEvent.withSchema(new URI("urn:cps:org.onap.cps:data-updated-event-schema:v1")); - cpsDataUpdatedEvent.withSource(new URI("urn:cps:org.onap.cps")); - cpsDataUpdatedEvent.withType("org.onap.cps.data-updated-event"); - return cpsDataUpdatedEvent; - } - - private Data createData(final DataNode dataNode, final String prefix) { - final Data data = new Data(); - DataMapUtils.toDataMapWithIdentifier(dataNode, prefix).forEach(data::setAdditionalProperty); - return data; - } - - private Content createContent(final Anchor anchor, final DataNode dataNode, - final OffsetDateTime observedTimestamp, final Operation operation) { - final var content = new Content(); - content.withAnchorName(anchor.getName()); - content.withDataspaceName(anchor.getDataspaceName()); - content.withSchemaSetName(anchor.getSchemaSetName()); - content.withOperation(Content.Operation.fromValue(operation.name())); - content.withObservedTimestamp( - DATE_TIME_FORMATTER.format(observedTimestamp == null ? OffsetDateTime.now() : observedTimestamp)); - if (dataNode != null) { - final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath()); - content.withData(createData(dataNode, prefix)); - } - return content; - } -} diff --git a/cps-service/src/main/java/org/onap/cps/notification/KafkaProducerListener.java b/cps-service/src/main/java/org/onap/cps/notification/KafkaProducerListener.java deleted file mode 100644 index f4b68c0699..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/KafkaProducerListener.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Bell Canada. All rights reserved. - * ================================================================================ - * 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.notification; - -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.springframework.kafka.support.ProducerListener; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class KafkaProducerListener<K, V> implements ProducerListener<K, V> { - - private NotificationErrorHandler notificationErrorHandler; - - public KafkaProducerListener(final NotificationErrorHandler notificationErrorHandler) { - this.notificationErrorHandler = notificationErrorHandler; - } - - @Override - public void onSuccess(final ProducerRecord<K, V> producerRecord, final RecordMetadata recordMetadata) { - log.debug("Message sent to event-bus topic :'{}' with body : {} ", producerRecord.topic(), - producerRecord.value()); - } - - @Override - public void onError(final ProducerRecord<K, V> producerRecord, - final RecordMetadata recordMetadata, - final Exception exception) { - notificationErrorHandler.onException("Failed to send message to message bus", - exception, producerRecord, recordMetadata); - } - -} diff --git a/cps-service/src/main/java/org/onap/cps/notification/NotificationErrorHandler.java b/cps-service/src/main/java/org/onap/cps/notification/NotificationErrorHandler.java deleted file mode 100644 index eef028d5f3..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/NotificationErrorHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Bell Canada. All rights reserved. - * ================================================================================ - * 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.notification; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -@Component -@Slf4j -public class NotificationErrorHandler { - - void onException(final Exception exception, final Object... context) { - onException("Failed to process", exception, context); - } - - void onException(final String message, final Exception exception, final Object... context) { - log.error("{} \n Error cause: {} \n Error context: {}", - message, - exception.getCause() != null ? exception.getCause().toString() : exception.getMessage(), - context, - exception); - } -} diff --git a/cps-service/src/main/java/org/onap/cps/notification/NotificationProperties.java b/cps-service/src/main/java/org/onap/cps/notification/NotificationProperties.java deleted file mode 100644 index b8a7144b3d..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/NotificationProperties.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Bell Canada. All rights reserved. - * ================================================================================ - * 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.notification; - -import jakarta.validation.constraints.NotNull; -import java.util.Collections; -import java.util.Map; -import lombok.Data; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; -import org.springframework.validation.annotation.Validated; - -@ConfigurationProperties(prefix = "notification.data-updated") -@Component -@Data -@Validated -public class NotificationProperties { - - @NotNull - private String topic; - private Map<String, String> filters = Collections.emptyMap(); - - @Value("${notification.enabled:true}") - private boolean enabled; -} diff --git a/cps-service/src/main/java/org/onap/cps/notification/NotificationPublisher.java b/cps-service/src/main/java/org/onap/cps/notification/NotificationPublisher.java deleted file mode 100644 index 2d87488245..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/NotificationPublisher.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. - * ================================================================================ - * 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.notification; - -import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.onap.cps.event.model.CpsDataUpdatedEvent; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.stereotype.Component; - -@Component -@Slf4j -public class NotificationPublisher { - - private KafkaTemplate<String, CpsDataUpdatedEvent> kafkaTemplate; - private String topicName; - - /** - * Create an instance of Notification Publisher. - * - * @param kafkaTemplate kafkaTemplate is send event using kafka - * @param topicName topic, to which cpsDataUpdatedEvent is sent, is provided by setting - * 'notification.data-updated.topic' in the application properties - */ - @Autowired - public NotificationPublisher( - final KafkaTemplate<String, CpsDataUpdatedEvent> kafkaTemplate, - final @Value("${notification.data-updated.topic}") String topicName) { - this.kafkaTemplate = kafkaTemplate; - this.topicName = topicName; - } - - /** - * Send event to Kafka with correct message key. - * - * @param cpsDataUpdatedEvent event to be sent to kafka - */ - public void sendNotification(@NonNull final CpsDataUpdatedEvent cpsDataUpdatedEvent) { - final var messageKey = cpsDataUpdatedEvent.getContent().getDataspaceName() + "," - + cpsDataUpdatedEvent.getContent().getAnchorName(); - log.debug("Data Updated event is being sent with messageKey: '{}' & body : {} ", - messageKey, cpsDataUpdatedEvent); - kafkaTemplate.send(topicName, messageKey, cpsDataUpdatedEvent); - } - -} diff --git a/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java b/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java deleted file mode 100644 index c29d042293..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2021-2022 Bell Canada. - * Modifications Copyright (C) 2022-2023 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.notification; - -import jakarta.annotation.PostConstruct; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.api.CpsAdminService; -import org.onap.cps.spi.model.Anchor; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class NotificationService { - - private final NotificationProperties notificationProperties; - private final NotificationPublisher notificationPublisher; - private final CpsDataUpdatedEventFactory cpsDataUpdatedEventFactory; - private final NotificationErrorHandler notificationErrorHandler; - private final CpsAdminService cpsAdminService; - private List<Pattern> dataspacePatterns; - - @PostConstruct - public void init() { - log.info("Notification Properties {}", notificationProperties); - this.dataspacePatterns = getDataspaceFilterPatterns(notificationProperties); - } - - private List<Pattern> getDataspaceFilterPatterns(final NotificationProperties notificationProperties) { - if (notificationProperties.isEnabled()) { - return Arrays.stream(notificationProperties.getFilters() - .getOrDefault("enabled-dataspaces", "") - .split(",")) - .map(filterPattern -> Pattern.compile(filterPattern, Pattern.CASE_INSENSITIVE)) - .collect(Collectors.toList()); - } else { - return Collections.emptyList(); - } - } - - /** - * Process Data Updated Event and publishes the notification. - * - * @param anchor anchor - * @param xpath xpath of changed data node - * @param operation operation - * @param observedTimestamp observedTimestamp - * @return future - */ - @Async("notificationExecutor") - public Future<Void> processDataUpdatedEvent(final Anchor anchor, final String xpath, final Operation operation, - final OffsetDateTime observedTimestamp) { - - log.debug("process data updated event for anchor '{}'", anchor); - try { - if (shouldSendNotification(anchor.getDataspaceName())) { - final var cpsDataUpdatedEvent = - cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, - observedTimestamp, getRootNodeOperation(xpath, operation)); - log.debug("data updated event to be published {}", cpsDataUpdatedEvent); - notificationPublisher.sendNotification(cpsDataUpdatedEvent); - } - } catch (final Exception exception) { - /* All the exceptions are handled to not to propagate it to caller. - CPS operation should not fail if sending event fails for any reason. - */ - notificationErrorHandler.onException("Failed to process cps-data-updated-event.", - exception, anchor, xpath, operation); - } - return CompletableFuture.completedFuture(null); - } - - /* - Add more complex rules based on dataspace and anchor later - */ - private boolean shouldSendNotification(final String dataspaceName) { - - return notificationProperties.isEnabled() - && dataspacePatterns.stream() - .anyMatch(pattern -> pattern.matcher(dataspaceName).find()); - } - - private Operation getRootNodeOperation(final String xpath, final Operation operation) { - return isRootXpath(xpath) || isRootContainerNodeXpath(xpath) ? operation : Operation.UPDATE; - } - - private static boolean isRootXpath(final String xpath) { - return "/".equals(xpath) || "".equals(xpath); - } - - private static boolean isRootContainerNodeXpath(final String xpath) { - return 0 == xpath.lastIndexOf('/'); - } - -} diff --git a/cps-service/src/main/java/org/onap/cps/notification/Operation.java b/cps-service/src/main/java/org/onap/cps/notification/Operation.java deleted file mode 100644 index 83e1ccf79f..0000000000 --- a/cps-service/src/main/java/org/onap/cps/notification/Operation.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2022 Bell Canada. - * ================================================================================ - * 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.notification; - -public enum Operation { - CREATE, - UPDATE, - DELETE -} diff --git a/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java b/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java index d58ddf4fa9..35dc7347b2 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java +++ b/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; -import org.onap.cps.api.CpsAdminService; +import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.impl.YangTextSchemaSourceSetCache; import org.onap.cps.cache.AnchorDataCacheEntry; import org.onap.cps.cpspath.parser.CpsPathPrefixType; @@ -48,7 +48,7 @@ public class PrefixResolver { private static final String CACHE_ENTRY_PROPERTY_NAME = "prefixPerContainerName"; - private final CpsAdminService cpsAdminService; + private final CpsAnchorService cpsAnchorService; private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; @@ -63,7 +63,7 @@ public class PrefixResolver { * @return the prefix of the module the top level element of given xpath */ public String getPrefix(final String dataspaceName, final String anchorName, final String xpath) { - final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); return getPrefix(anchor, xpath); } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy index 12564fb6d4..3546b81671 100755..100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy @@ -1,9 +1,6 @@ /* - * ============LICENSE_START======================================================= - * Copyright (C) 2020-2023 Nordix Foundation - * Modifications Copyright (C) 2020-2022 Bell Canada. - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2022 TechMahindra Ltd. + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,29 +20,20 @@ package org.onap.cps.api.impl -import org.onap.cps.api.CpsDataService import org.onap.cps.spi.CpsAdminPersistenceService +import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException import org.onap.cps.spi.model.Anchor -import org.onap.cps.spi.model.Dataspace import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification -import java.time.OffsetDateTime -class CpsAdminServiceImplSpec extends Specification { +class CpsAnchorServiceImplSpec extends Specification { + def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService) - def mockCpsDataService = Mock(CpsDataService) + def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) def mockCpsValidator = Mock(CpsValidator) - def objectUnderTest = new CpsAdminServiceImpl(mockCpsAdminPersistenceService, mockCpsDataService,mockCpsValidator) - def 'Create dataspace method invokes persistence service.'() { - when: 'create dataspace method is invoked' - objectUnderTest.createDataspace('someDataspace') - then: 'the persistence service method is invoked with same parameters' - 1 * mockCpsAdminPersistenceService.createDataspace('someDataspace') - and: 'the CpsValidator is called on the dataspaceName' - 1 * mockCpsValidator.validateNameCharacters('someDataspace') - } + def objectUnderTest = new CpsAnchorServiceImpl(mockCpsAdminPersistenceService, mockCpsDataPersistenceService, mockCpsValidator) def 'Create anchor method invokes persistence service.'() { when: 'create anchor method is invoked' @@ -105,29 +93,13 @@ class CpsAdminServiceImplSpec extends Specification { 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someAnchor') } - def 'Retrieve dataspace.'() { - given: 'a dataspace is already created' - def dataspace = new Dataspace(name: "someDataspace") - mockCpsAdminPersistenceService.getDataspace('someDataspace') >> dataspace - expect: 'the dataspace provided by persistence service is returned as result' - assert objectUnderTest.getDataspace('someDataspace') == dataspace - } - - def 'Retrieve all dataspaces.'() { - given: 'that all given dataspaces are already created' - def dataspaces = [new Dataspace(name: "test-dataspace1"), new Dataspace(name: "test-dataspace2")] - mockCpsAdminPersistenceService.getAllDataspaces() >> dataspaces - expect: 'the dataspace provided by persistence service is returned as result' - assert objectUnderTest.getAllDataspaces() == dataspaces - } - def 'Delete anchor.'() { when: 'delete anchor is invoked' objectUnderTest.deleteAnchor('someDataspace','someAnchor') then: 'delete data nodes is invoked on the data service with expected parameters' - 1 * mockCpsDataService.deleteDataNodes('someDataspace','someAnchor', _ as OffsetDateTime ) + 1 * mockCpsDataPersistenceService.deleteDataNodes('someDataspace','someAnchor') and: 'the persistence service method is invoked with same parameters to delete anchor' - 1 * mockCpsAdminPersistenceService.deleteAnchor('someDataspace','someAnchor') + 1 * mockCpsAdminPersistenceService.deleteAnchor('someDataspace','someAnchor') and: 'the CpsValidator is called on the dataspaceName, anchorName' 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someAnchor') } @@ -136,7 +108,7 @@ class CpsAdminServiceImplSpec extends Specification { when: 'delete anchors is invoked' objectUnderTest.deleteAnchors('someDataspace', ['anchor1', 'anchor2']) then: 'delete data nodes is invoked on the data service with expected parameters' - 1 * mockCpsDataService.deleteDataNodes('someDataspace', _ as Collection<String>, _ as OffsetDateTime) + 1 * mockCpsDataPersistenceService.deleteDataNodes('someDataspace', _ as Collection<String>) and: 'the persistence service method is invoked with same parameters to delete anchor' 1 * mockCpsAdminPersistenceService.deleteAnchors('someDataspace',_ as Collection<String>) and: 'the CpsValidator is called on the dataspace name and anchor names' @@ -157,7 +129,7 @@ class CpsAdminServiceImplSpec extends Specification { def 'Query all anchors with Module Names Not Found Exception in persistence layer.'() { given: 'the persistence layer throws a Module Names Not Found Exception' - def originalException = new ModuleNamesNotFoundException('exception-ds', [ 'm1', 'm2']) + def originalException = new ModuleNamesNotFoundException('exception-ds', ['m1', 'm2']) mockCpsAdminPersistenceService.queryAnchors(*_) >> { throw originalException} when: 'attempt query anchors' objectUnderTest.queryAnchorNames('some-dataspace-name', []) @@ -170,19 +142,11 @@ class CpsAdminServiceImplSpec extends Specification { assert thrownUp.details.contains('m2') } - def 'Delete dataspace.'() { - when: 'delete dataspace is invoked' - objectUnderTest.deleteDataspace('someDataspace') - then: 'associated persistence service method is invoked with correct parameter' - 1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace') - and: 'the CpsValidator is called on the dataspaceName' - 1 * mockCpsValidator.validateNameCharacters('someDataspace') - } - def 'Update anchor schema set.'() { when: 'update anchor is invoked' objectUnderTest.updateAnchorSchemaSet('someDataspace', 'someAnchor', 'someSchemaSetName') then: 'associated persistence service method is invoked with correct parameter' 1 * mockCpsAdminPersistenceService.updateAnchorSchemaSet('someDataspace', 'someAnchor', 'someSchemaSetName') } + } 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 a914598521..77e15c320e 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 @@ -24,10 +24,8 @@ package org.onap.cps.api.impl import org.onap.cps.TestUtils -import org.onap.cps.api.CpsAdminService +import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDeltaService -import org.onap.cps.notification.NotificationService -import org.onap.cps.notification.Operation import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.ConcurrencyException @@ -38,7 +36,6 @@ import org.onap.cps.spi.exceptions.SessionTimeoutException import org.onap.cps.spi.model.Anchor 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.spi.utils.CpsValidator import org.onap.cps.utils.ContentType import org.onap.cps.utils.TimedYangParser @@ -46,27 +43,24 @@ import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import spock.lang.Shared import spock.lang.Specification - import java.time.OffsetDateTime import java.util.stream.Collectors class CpsDataServiceImplSpec extends Specification { def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) - def mockCpsAdminService = Mock(CpsAdminService) + def mockCpsAnchorService = Mock(CpsAnchorService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) - def mockNotificationService = Mock(NotificationService) def mockCpsValidator = Mock(CpsValidator) def timedYangParser = new TimedYangParser() def mockCpsDeltaService = Mock(CpsDeltaService); - def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAdminService, - mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator, timedYangParser, mockCpsDeltaService) + def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAnchorService, + mockYangTextSchemaSourceSetCache, mockCpsValidator, timedYangParser, mockCpsDeltaService) def setup() { - - mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor - mockCpsAdminService.getAnchor(dataspaceName, ANCHOR_NAME_1) >> anchor1 - mockCpsAdminService.getAnchor(dataspaceName, ANCHOR_NAME_2) >> anchor2 + mockCpsAnchorService.getAnchor(dataspaceName, anchorName) >> anchor + mockCpsAnchorService.getAnchor(dataspaceName, ANCHOR_NAME_1) >> anchor1 + mockCpsAnchorService.getAnchor(dataspaceName, ANCHOR_NAME_2) >> anchor2 } @Shared @@ -92,8 +86,6 @@ class CpsDataServiceImplSpec extends Specification { { dataNode -> dataNode.xpath[0] == '/test-tree' }) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.CREATE, observedTimestamp) where: 'given parameters' scenario | dataFile | contentType 'json' | 'test-tree.json' | ContentType.JSON @@ -115,18 +107,6 @@ class CpsDataServiceImplSpec extends Specification { 'invalid xml' | '<invalid xml' | ContentType.XML || 'Failed to parse xml data' } - def 'Saving #scenarioDesired data exception during notification.'() { - given: 'schema set for given anchor and dataspace references test-tree model' - setupSchemaSetMocks('test-tree.yang') - and: 'the notification service throws an exception' - mockNotificationService.processDataUpdatedEvent(*_) >> { throw new RuntimeException('to be ignored')} - when: 'save data method is invoked with test-tree json data' - def data = TestUtils.getResourceFileContent('test-tree.json') - objectUnderTest.saveData(dataspaceName, anchorName, data, observedTimestamp) - then: 'the exception is ignored' - noExceptionThrown() - } - def 'Saving list element data fragment under Root node.'() { given: 'schema set for given anchor and dataspace references bookstore model' setupSchemaSetMocks('bookstore.yang') @@ -145,8 +125,6 @@ class CpsDataServiceImplSpec extends Specification { ) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.UPDATE, observedTimestamp) } def 'Saving child data fragment under existing node.'() { @@ -160,8 +138,6 @@ class CpsDataServiceImplSpec extends Specification { { dataNode -> dataNode.xpath[0] == '/test-tree/branch[@name=\'New\']' }) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.CREATE, observedTimestamp) } def 'Saving list element data fragment under existing node.'() { @@ -182,8 +158,6 @@ class CpsDataServiceImplSpec extends Specification { ) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp) } def 'Saving collection of a batch with data fragment under existing node.'() { @@ -202,8 +176,6 @@ class CpsDataServiceImplSpec extends Specification { assert listOfXpaths.containsAll(['/test-tree/branch[@name=\'B\']','/test-tree/branch[@name=\'A\']']) } } - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp) } def 'Saving empty list element data fragment.'() { @@ -266,8 +238,6 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, {dataNode -> dataNode.keySet()[0] == expectedNodeXpath}) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp) where: 'following parameters were used' scenario | parentNodeXpath | jsonData || expectedNodeXpath 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' @@ -300,8 +270,6 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, {dataNode -> dataNode.keySet()[index] == expectedNodeXpath}) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp) where: 'the following parameters were used' index | expectedNodeXpath 0 | '/first-container' @@ -325,8 +293,6 @@ class CpsDataServiceImplSpec extends Specification { .iterator().next() == "/bookstore/categories[@code='01']/books[@title='new']"}) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'the data updated event is sent to the notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/bookstore', Operation.UPDATE, observedTimestamp) } def 'Replace data node using singular data node: #scenario.'() { @@ -337,8 +303,6 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' @@ -356,10 +320,6 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) - and: 'data updated event is sent to notification service' - nodesJsonData.keySet().each { - 1 * mockNotificationService.processDataUpdatedEvent(anchor, it, Operation.UPDATE, observedTimestamp) - } and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' @@ -399,8 +359,6 @@ class CpsDataServiceImplSpec extends Specification { ) and: 'the CpsValidator is called on the dataspaceName and AnchorName twice' 2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp) } def 'Replace whole list content with empty list element.'() { @@ -420,8 +378,6 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch') and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree/branch', Operation.DELETE, observedTimestamp) } def 'Delete multiple list elements under existing node.'() { @@ -431,8 +387,6 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName, ['/test-tree/branch[@name="A"]', '/test-tree/branch[@name="B"]']) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'two data updated events are sent to notification service' - 2 * mockNotificationService.processDataUpdatedEvent(anchor, _, Operation.DELETE, observedTimestamp) } def 'Delete data node under anchor and dataspace.'() { @@ -442,16 +396,12 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node') and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) - and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/data-node', Operation.DELETE, observedTimestamp) } def 'Delete all data nodes for a given anchor and dataspace.'() { when: 'delete data nodes method is invoked with correct parameters' objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp) - then: 'data updated event is sent to notification service before the delete' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.DELETE, observedTimestamp) - and: 'the CpsValidator is called on the dataspaceName and AnchorName' + then: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'the persistence service method is invoked with the correct parameters' 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName) @@ -474,14 +424,12 @@ class CpsDataServiceImplSpec extends Specification { def 'Delete all data nodes for given dataspace and multiple anchors.'() { given: 'schema set for given anchors and dataspace references test tree model' setupSchemaSetMocks('test-tree.yang') - mockCpsAdminService.getAnchors(dataspaceName, ['anchor1', 'anchor2']) >> + mockCpsAnchorService.getAnchors(dataspaceName, ['anchor1', 'anchor2']) >> [new Anchor(name: 'anchor1', dataspaceName: dataspaceName), new Anchor(name: 'anchor2', dataspaceName: dataspaceName)] when: 'delete data node method is invoked with correct parameters' objectUnderTest.deleteDataNodes(dataspaceName, ['anchor1', 'anchor2'], observedTimestamp) - then: 'data updated events are sent to notification service before the delete' - 2 * mockNotificationService.processDataUpdatedEvent(_, '/', Operation.DELETE, observedTimestamp) - and: 'the CpsValidator is called on the dataspace name and the anchor names' + then: 'the CpsValidator is called on the dataspace name and the anchor names' 2 * mockCpsValidator.validateNameCharacters(_) and: 'the persistence service method is invoked with the correct parameters' 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, _ as Collection<String>) diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy new file mode 100644 index 0000000000..8e17594bd1 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy @@ -0,0 +1,67 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.api.impl + +import org.onap.cps.spi.CpsAdminPersistenceService +import org.onap.cps.spi.model.Dataspace +import org.onap.cps.spi.utils.CpsValidator +import spock.lang.Specification + +class CpsDataspaceServiceImplSpec extends Specification { + def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService) + def mockCpsValidator = Mock(CpsValidator) + def objectUnderTest = new CpsDataspaceServiceImpl(mockCpsAdminPersistenceService,mockCpsValidator) + + def 'Create dataspace method invokes persistence service.'() { + when: 'create dataspace method is invoked' + objectUnderTest.createDataspace('someDataspace') + then: 'the persistence service method is invoked with same parameters' + 1 * mockCpsAdminPersistenceService.createDataspace('someDataspace') + and: 'the CpsValidator is called on the dataspaceName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace') + } + + def 'Retrieve dataspace.'() { + given: 'a dataspace is already created' + def dataspace = new Dataspace(name: "someDataspace") + mockCpsAdminPersistenceService.getDataspace('someDataspace') >> dataspace + expect: 'the dataspace provided by persistence service is returned as result' + assert objectUnderTest.getDataspace('someDataspace') == dataspace + } + + def 'Retrieve all dataspaces.'() { + given: 'that all given dataspaces are already created' + def dataspaces = [new Dataspace(name: "test-dataspace1"), new Dataspace(name: "test-dataspace2")] + mockCpsAdminPersistenceService.getAllDataspaces() >> dataspaces + expect: 'the dataspace provided by persistence service is returned as result' + assert objectUnderTest.getAllDataspaces() == dataspaces + } + + def 'Delete dataspace.'() { + when: 'delete dataspace is invoked' + objectUnderTest.deleteDataspace('someDataspace') + then: 'associated persistence service method is invoked with correct parameter' + 1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace') + and: 'the CpsValidator is called on the dataspaceName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace') + } + +} 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 d8edb02abd..d909e27abf 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 @@ -23,11 +23,12 @@ package org.onap.cps.api.impl +import org.onap.cps.api.CpsAnchorService + import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED import org.onap.cps.TestUtils -import org.onap.cps.api.CpsAdminService import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.exceptions.DuplicatedYangResourceException import org.onap.cps.spi.exceptions.ModelValidationException @@ -45,12 +46,12 @@ import spock.lang.Specification class CpsModuleServiceImplSpec extends Specification { def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService) - def mockCpsAdminService = Mock(CpsAdminService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) + def mockCpsAnchorService = Mock(CpsAnchorService) def mockCpsValidator = Mock(CpsValidator) def timedYangTextSchemaSourceSetBuilder = new TimedYangTextSchemaSourceSetBuilder() - def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder) + def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder) def 'Create schema set.'() { when: 'Create schema set method is invoked' @@ -133,11 +134,11 @@ class CpsModuleServiceImplSpec extends Specification { def 'Delete schema-set when cascade is allowed.'() { given: '#numberOfAnchors anchors are associated with schemaset' def associatedAnchors = createAnchors(numberOfAnchors) - mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> associatedAnchors + mockCpsAnchorService.getAnchors('my-dataspace', 'my-schemaset') >> associatedAnchors when: 'schema set deletion is requested with cascade allowed' objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_ALLOWED) then: 'anchor deletion is called #numberOfAnchors times' - numberOfAnchors * mockCpsAdminService.deleteAnchor('my-dataspace', _) + numberOfAnchors * mockCpsAnchorService.deleteAnchor('my-dataspace', _) and: 'persistence service method is invoked with same parameters' 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset') and: 'schema set will be removed from the cache' @@ -152,11 +153,11 @@ class CpsModuleServiceImplSpec extends Specification { def 'Delete schema-set when cascade is prohibited.'() { given: 'no anchors are associated with schemaset' - mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> Collections.emptyList() + mockCpsAnchorService.getAnchors('my-dataspace', 'my-schemaset') >> Collections.emptyList() when: 'schema set deletion is requested with cascade allowed' objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_PROHIBITED) then: 'no anchors are deleted' - 0 * mockCpsAdminService.deleteAnchor(_, _) + 0 * mockCpsAnchorService.deleteAnchor(_, _) and: 'persistence service method is invoked with same parameters' 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset') and: 'schema set will be removed from the cache' @@ -169,7 +170,7 @@ class CpsModuleServiceImplSpec extends Specification { def 'Delete schema-set when cascade is prohibited and schema-set has anchors.'() { given: '2 anchors are associated with schemaset' - mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> createAnchors(2) + mockCpsAnchorService.getAnchors('my-dataspace', 'my-schemaset') >> createAnchors(2) when: 'schema set deletion is requested with cascade allowed' objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_PROHIBITED) then: 'Schema-Set in Use exception is thrown' @@ -178,11 +179,11 @@ class CpsModuleServiceImplSpec extends Specification { def 'Delete multiple schema-sets when cascade is allowed.'() { given: '#numberOfAnchors anchors are associated with each schemaset' - mockCpsAdminService.getAnchors('my-dataspace', ['my-schemaset1', 'my-schemaset2']) >> createAnchors(numberOfAnchors * 2) + mockCpsAnchorService.getAnchors('my-dataspace', ['my-schemaset1', 'my-schemaset2']) >> createAnchors(numberOfAnchors * 2) when: 'schema set deletion is requested with cascade allowed' objectUnderTest.deleteSchemaSetsWithCascade('my-dataspace', ['my-schemaset1', 'my-schemaset2']) then: 'anchor deletion is called #numberOfAnchors times' - mockCpsAdminService.deleteAnchors('my-dataspace', _) + mockCpsAnchorService.deleteAnchors('my-dataspace', _) and: 'persistence service method is invoked with same parameters' mockCpsModulePersistenceService.deleteSchemaSets('my-dataspace', _) and: 'schema sets will be removed from the cache' diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy index 1b873ec12b..4782468f19 100755 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy @@ -24,9 +24,9 @@ package org.onap.cps.api.impl
import org.onap.cps.TestUtils
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDeltaService
-import org.onap.cps.notification.NotificationService
+import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.model.Anchor
@@ -40,8 +40,7 @@ import spock.lang.Specification class E2ENetworkSliceSpec extends Specification {
def mockModuleStoreService = Mock(CpsModulePersistenceService)
def mockDataStoreService = Mock(CpsDataPersistenceService)
- def mockCpsAdminService = Mock(CpsAdminService)
- def mockNotificationService = Mock(NotificationService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
def mockCpsValidator = Mock(CpsValidator)
def timedYangTextSchemaSourceSetBuilder = new TimedYangTextSchemaSourceSetBuilder()
@@ -49,10 +48,10 @@ class E2ENetworkSliceSpec extends Specification { def mockCpsDeltaService = Mock(CpsDeltaService)
def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockModuleStoreService,
- mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
+ mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
- def cpsDataServiceImpl = new CpsDataServiceImpl(mockDataStoreService, mockCpsAdminService,
- mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator, timedYangParser, mockCpsDeltaService)
+ def cpsDataServiceImpl = new CpsDataServiceImpl(mockDataStoreService, mockCpsAnchorService,
+ mockYangTextSchemaSourceSetCache, mockCpsValidator, timedYangParser, mockCpsDeltaService)
def dataspaceName = 'someDataspace'
def anchorName = 'someAnchor'
@@ -91,7 +90,7 @@ class E2ENetworkSliceSpec extends Specification { and : 'a valid json is provided for the model'
def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-Cavsta-Data.txt')
and : 'all the further dependencies are mocked '
- mockCpsAdminService.getAnchor(dataspaceName, anchorName) >>
+ mockCpsAnchorService.getAnchor(dataspaceName, anchorName) >>
new Anchor().builder().name(anchorName).schemaSetName(schemaSetName).dataspaceName(dataspaceName).build()
mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >>
YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
@@ -124,7 +123,7 @@ class E2ENetworkSliceSpec extends Specification { and : 'a valid json is provided for the model'
def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-ran-inventory-data.json')
and : 'all the further dependencies are mocked '
- mockCpsAdminService.getAnchor('someDataspace', 'someAnchor') >>
+ mockCpsAnchorService.getAnchor('someDataspace', 'someAnchor') >>
new Anchor().builder().name('someAnchor').schemaSetName('someSchemaSet').dataspaceName(dataspaceName).build()
mockYangTextSchemaSourceSetCache.get('someDataspace', 'someSchemaSet') >> YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
mockModuleStoreService.getYangSchemaResources('someDataspace', 'someSchemaSet') >> schemaContext
diff --git a/cps-service/src/test/groovy/org/onap/cps/config/AsyncConfigSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/config/AsyncConfigSpec.groovy new file mode 100644 index 0000000000..9f4e81aabc --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/config/AsyncConfigSpec.groovy @@ -0,0 +1,46 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.config + +import spock.lang.Specification + +class AsyncConfigSpec extends Specification { + + def objectUnderTest = new AsyncConfig() + + def 'Create Async Config and validate it'() { + when: 'we set some test properties to tune taskexecutor' + objectUnderTest.setCorePoolSize(5) + objectUnderTest.setMaxPoolSize(50) + objectUnderTest.setQueueCapacity(100) + objectUnderTest.setThreadNamePrefix('Test-') + objectUnderTest.setWaitForTasksToCompleteOnShutdown(true) + then: 'we can instantiate a Async Config object' + assert objectUnderTest != null + and: 'taskexector is configured with correct properties' + def tasExecutor = objectUnderTest.getThreadAsyncExecutorForNotification() + assert tasExecutor.properties['corePoolSize'] == 5 + assert tasExecutor.properties['maxPoolSize'] == 50 + assert tasExecutor.properties['queueCapacity'] == 100 + assert tasExecutor.properties['keepAliveSeconds'] == 60 + assert tasExecutor.properties['threadNamePrefix'] == 'Test-' + } +} diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdatedEventFactorySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdatedEventFactorySpec.groovy deleted file mode 100644 index 49f4bf3850..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdatedEventFactorySpec.groovy +++ /dev/null @@ -1,142 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2021-2022 Bell Canada. - * Modifications Copyright (c) 2022-2023 Nordix Foundation - * Modifications Copyright (C) 2023 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.notification - -import org.onap.cps.spi.model.DataNode - -import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter -import org.onap.cps.utils.DateTimeUtility -import org.onap.cps.utils.PrefixResolver -import org.onap.cps.api.CpsDataService -import org.onap.cps.event.model.Content -import org.onap.cps.event.model.Data -import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.model.Anchor -import org.onap.cps.spi.model.DataNodeBuilder -import org.springframework.util.StringUtils -import spock.lang.Specification - -class CpsDataUpdatedEventFactorySpec extends Specification { - - def mockCpsDataService = Mock(CpsDataService) - - def mockPrefixResolver = Mock(PrefixResolver) - - def objectUnderTest = new CpsDataUpdatedEventFactory(mockCpsDataService, mockPrefixResolver) - - def dateTimeFormat = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ' - - def 'Create a CPS data updated event successfully: #scenario'() { - given: 'an anchor which has been updated' - def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name') - and: 'cps data service returns the data node details' - def xpath = '/xpath' - def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(['leafName': 'leafValue']).build() - mockCpsDataService.getDataNodes( - 'my-dataspace', 'my-anchorname', '/', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode] - when: 'CPS data updated event is created' - def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(anchor, - DateTimeUtility.toOffsetDateTime(inputObservedTimestamp), Operation.CREATE) - then: 'CPS data updated event is created with correct envelope' - with(cpsDataUpdatedEvent) { - type == 'org.onap.cps.data-updated-event' - source == new URI('urn:cps:org.onap.cps') - schema == new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1') - StringUtils.hasText(id) - content != null - } - and: 'correct content' - with(cpsDataUpdatedEvent.content) { - assert isExpectedDateTimeFormat(observedTimestamp): "$observedTimestamp is not in $dateTimeFormat format" - if (inputObservedTimestamp != null) - assert observedTimestamp == inputObservedTimestamp - else - assert OffsetDateTime.now().minusSeconds(20).isBefore( - DateTimeUtility.toOffsetDateTime(observedTimestamp)) - assert anchorName == 'my-anchorname' - assert dataspaceName == 'my-dataspace' - assert schemaSetName == 'my-schemaset-name' - assert operation == Content.Operation.CREATE - assert data == new Data().withAdditionalProperty('xpath', ['leafName': 'leafValue']) - } - where: - scenario | inputObservedTimestamp - 'with observed timestamp -0400' | '2021-01-01T23:00:00.345-0400' - 'with observed timestamp +0400' | '2021-01-01T23:00:00.345+0400' - 'missing observed timestamp' | null - } - - def 'Create a delete CPS data updated event successfully'() { - given: 'an anchor which has been deleted' - def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name') - def deletionTimestamp = '2021-01-01T23:00:00.345-0400' - when: 'a delete root data node event is created' - def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(anchor, - DateTimeUtility.toOffsetDateTime(deletionTimestamp), Operation.DELETE) - then: 'CPS data updated event is created with correct envelope' - with(cpsDataUpdatedEvent) { - type == 'org.onap.cps.data-updated-event' - source == new URI('urn:cps:org.onap.cps') - schema == new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1') - StringUtils.hasText(id) - content != null - } - and: 'correct content' - with(cpsDataUpdatedEvent.content) { - assert isExpectedDateTimeFormat(observedTimestamp): "$observedTimestamp is not in $dateTimeFormat format" - assert observedTimestamp == deletionTimestamp - assert anchorName == 'my-anchorname' - assert dataspaceName == 'my-dataspace' - assert schemaSetName == 'my-schemaset-name' - assert operation == Content.Operation.DELETE - assert data == null - } - } - - def 'Create CPS Data Event with URI Syntax Exception'() { - given: 'an anchor' - def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name') - and: 'a mocked data Node (collection)' - def mockDataNode = Mock(DataNode) - mockCpsDataService.getDataNodes(*_) >> [ mockDataNode ] - and: 'a URI syntax exception is thrown somewhere (using datanode as cannot manipulate hardcoded URIs' - def originalException = new URISyntaxException('input', 'reason', 0) - mockDataNode.getXpath() >> { throw originalException } - when: 'attempt to create data updated event' - objectUnderTest.createCpsDataUpdatedEvent(anchor, OffsetDateTime.now(), Operation.UPDATE) - then: 'the same exception is thrown up' - def thrownUp = thrown(URISyntaxException) - assert thrownUp == originalException - } - - def isExpectedDateTimeFormat(String observedTimestamp) { - try { - DateTimeFormatter.ofPattern(dateTimeFormat).parse(observedTimestamp) - } catch (DateTimeParseException) { - return false - } - return true - } - -} diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/KafkaPublisherSpecBase.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/KafkaPublisherSpecBase.groovy deleted file mode 100644 index b60b38f054..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/notification/KafkaPublisherSpecBase.groovy +++ /dev/null @@ -1,93 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Bell Canada. All rights reserved. - * ================================================================================ - * 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.notification - -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.kafka.config.TopicBuilder -import org.springframework.kafka.core.ConsumerFactory -import org.springframework.kafka.core.KafkaAdmin -import org.springframework.kafka.core.KafkaTemplate -import org.springframework.kafka.listener.ConcurrentMessageListenerContainer -import org.springframework.kafka.listener.ContainerProperties -import org.springframework.kafka.listener.MessageListener -import org.springframework.kafka.test.utils.ContainerTestUtils -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.context.DynamicPropertyRegistry -import org.springframework.test.context.DynamicPropertySource -import spock.lang.Shared -import spock.lang.Specification - -@ContextConfiguration(classes = [KafkaAutoConfiguration, KafkaProducerListener, NotificationErrorHandler]) -@SpringBootTest -class KafkaPublisherSpecBase extends Specification { - - @Autowired - KafkaTemplate kafkaTemplate - - @Autowired - KafkaAdmin kafkaAdmin - - @Autowired - ConsumerFactory consumerFactory - - @Shared volatile topicCreated = false - @Shared consumedMessages = new ArrayList<>() - - def cpsEventTopic = 'cps-events' - - @DynamicPropertySource - static void registerKafkaProperties(DynamicPropertyRegistry registry) { - registry.add("spring.kafka.bootstrap-servers", KafkaTestContainerConfig::getBootstrapServers) - } - - def setup() { - // Kafka listener and topic should be created only once for a test-suite. - // We are also dependent on sprint context to achieve it, and can not execute it in setupSpec - if (!topicCreated) { - kafkaAdmin.createOrModifyTopics(TopicBuilder.name(cpsEventTopic).partitions(1).replicas(1).build()) - startListeningToTopic() - topicCreated = true - } - /* kafka message listener stores the messages to consumedMessages. - It is important to clear the list before each test case so that test cases can fetch the message from index '0'. - */ - consumedMessages.clear() - } - - def startListeningToTopic() { - ContainerProperties containerProperties = new ContainerProperties(cpsEventTopic) - containerProperties.setMessageListener([ - onMessage: { - record -> - consumedMessages.add(record.value()) - }] as MessageListener) - - ConcurrentMessageListenerContainer container = - new ConcurrentMessageListenerContainer<>( - consumerFactory, - containerProperties) - - container.start() - ContainerTestUtils.waitForAssignment(container, 1) - } - -} diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy deleted file mode 100644 index b07b31a35b..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy +++ /dev/null @@ -1,49 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Bell Canada. All rights reserved. - * ================================================================================ - * 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.notification - -import org.testcontainers.containers.KafkaContainer -import org.testcontainers.utility.DockerImageName - -class KafkaTestContainerConfig { - - private static KafkaContainer kafkaContainer - - static { - getKafkaContainer() - } - - // Not the best performance but it is good enough for test case - private static synchronized KafkaContainer getKafkaContainer() { - if (kafkaContainer == null) { - kafkaContainer = new KafkaContainer(DockerImageName.parse("registry.nordix.org/onaptest/confluentinc/cp-kafka:6.2.1").asCompatibleSubstituteFor("confluentinc/cp-kafka")) - .withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "false") - kafkaContainer.start() - Runtime.getRuntime().addShutdownHook(new Thread(kafkaContainer::stop)) - } - return kafkaContainer - } - - static String getBootstrapServers() { - getKafkaContainer() - return kafkaContainer.getBootstrapServers() - } - -} diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy deleted file mode 100644 index 89e305aedb..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy +++ /dev/null @@ -1,60 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 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.notification - -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.slf4j.LoggerFactory - -import spock.lang.Specification - -class NotificationErrorHandlerSpec extends Specification{ - - NotificationErrorHandler objectUnderTest = new NotificationErrorHandler() - def logWatcher = Spy(ListAppender<ILoggingEvent>) - - @BeforeEach - void setup() { - ((Logger) LoggerFactory.getLogger(NotificationErrorHandler.class)).addAppender(logWatcher); - logWatcher.start(); - } - - @AfterEach - void teardown() { - ((Logger) LoggerFactory.getLogger(NotificationErrorHandler.class)).detachAndStopAllAppenders(); - } - - def 'Logging exception via notification error handler #scenario'() { - when: 'exception #scenario occurs' - objectUnderTest.onException(exception, 'some context') - then: 'log output results contains the correct error details' - def logMessage = logWatcher.list[0].getFormattedMessage() - assert logMessage.contains('Failed to process') - assert logMessage.contains("Error cause: ${exptectedCauseString}") - assert logMessage.contains("Error context: [some context]") - where: - scenario | exception || exptectedCauseString - 'with cause' | new Exception('message') || 'message' - 'without cause' | new Exception('message', new RuntimeException('cause')) || 'java.lang.RuntimeException: cause' - } -} diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationPublisherSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationPublisherSpec.groovy deleted file mode 100644 index 6cd9ae1b20..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationPublisherSpec.groovy +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Bell Canada. All rights reserved. - * Modifications Copyright (C) 2021-2022 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.notification - -import org.apache.kafka.clients.producer.ProducerRecord -import org.onap.cps.event.model.Content -import org.onap.cps.event.model.CpsDataUpdatedEvent -import org.spockframework.spring.SpringBean -import org.springframework.kafka.KafkaException -import org.springframework.kafka.core.KafkaTemplate -import spock.util.concurrent.PollingConditions - -class NotificationPublisherSpec extends KafkaPublisherSpecBase { - - @SpringBean - NotificationErrorHandler spyNotificationErrorHandler = Spy(new NotificationErrorHandler()) - - @SpringBean - KafkaProducerListener spyKafkaProducerListener = Spy(new KafkaProducerListener<>(spyNotificationErrorHandler)) - - KafkaTemplate spyKafkaTemplate - NotificationPublisher objectUnderTest - - def myAnchorName = 'my-anchor' - def myDataspaceName = 'my-dataspace' - - def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() - .withContent(new Content() - .withDataspaceName(myDataspaceName) - .withAnchorName(myAnchorName)) - - def setup() { - spyKafkaTemplate = Spy(kafkaTemplate) - objectUnderTest = new NotificationPublisher(spyKafkaTemplate, cpsEventTopic); - } - - def 'Sending event to message bus with correct message Key.'() { - - when: 'event is sent to publisher' - objectUnderTest.sendNotification(cpsDataUpdatedEvent) - kafkaTemplate.flush() - - then: 'event is sent to correct topic with the expected messageKey' - interaction { - def messageKey = myDataspaceName + "," + myAnchorName - 1 * spyKafkaTemplate.send(cpsEventTopic, messageKey, cpsDataUpdatedEvent) - } - and: 'received a successful response' - 1 * spyKafkaProducerListener.onSuccess(_ as ProducerRecord, _) - and: 'kafka consumer returns expected message' - def conditions = new PollingConditions(timeout: 60, initialDelay: 0, factor: 1) - conditions.eventually { - assert cpsDataUpdatedEvent == consumedMessages.get(0) - } - } - - def 'Handling of async errors from message bus.'() { - given: 'topic does not exist' - objectUnderTest.topicName = 'non-existing-topic' - - when: 'message to sent to a non-existing topic' - objectUnderTest.sendNotification(cpsDataUpdatedEvent) - kafkaTemplate.flush() - - then: 'error is thrown' - thrown KafkaException - and: 'error handler is called with exception details' - 1 * spyKafkaProducerListener.onError(_ as ProducerRecord, _, _ as Exception) - 1 * spyNotificationErrorHandler.onException(_ as String, _ as Exception, - _ as ProducerRecord, _) - } - -} diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy deleted file mode 100644 index f07f89b391..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy +++ /dev/null @@ -1,158 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2021-2022 Bell Canada. - * Modifications Copyright (C) 2022-2023 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.notification - -import org.onap.cps.api.CpsAdminService -import org.onap.cps.config.AsyncConfig -import org.onap.cps.event.model.CpsDataUpdatedEvent -import org.onap.cps.spi.model.Anchor -import org.spockframework.spring.SpringBean -import org.spockframework.spring.SpringSpy -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.context.properties.EnableConfigurationProperties -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.ContextConfiguration -import spock.lang.Shared -import spock.lang.Specification - -import java.time.OffsetDateTime -import java.util.concurrent.TimeUnit - -@SpringBootTest -@EnableConfigurationProperties -@ContextConfiguration(classes = [NotificationProperties, NotificationService, NotificationErrorHandler, AsyncConfig]) -class NotificationServiceSpec extends Specification { - - @SpringSpy - NotificationProperties spyNotificationProperties - @SpringBean - NotificationPublisher mockNotificationPublisher = Mock() - @SpringBean - CpsDataUpdatedEventFactory mockCpsDataUpdatedEventFactory = Mock() - @SpringSpy - NotificationErrorHandler spyNotificationErrorHandler - @SpringBean - CpsAdminService mockCpsAdminService = Mock() - - @Autowired - NotificationService objectUnderTest - - @Shared - def dataspaceName = 'my-dataspace-published' - @Shared - def anchorName = 'my-anchorname' - @Shared - def anchor = new Anchor('my-anchorname', 'my-dataspace-published', 'my-schemaset-name') - def myObservedTimestamp = OffsetDateTime.now() - - def setup() { - mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor - } - - def 'Skip sending notification when disabled.'() { - given: 'notification is disabled' - spyNotificationProperties.isEnabled() >> false - when: 'dataUpdatedEvent is received' - objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp) - then: 'the notification is not sent' - 0 * mockNotificationPublisher.sendNotification(_) - } - - def 'Send notification when enabled: #scenario.'() { - given: 'notification is enabled' - spyNotificationProperties.isEnabled() >> true - and: 'an anchor is in dataspace where #scenario' - def anchor = new Anchor('my-anchorname', dataspaceName, 'my-schemaset-name') - and: 'event factory can create event successfully' - def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >> cpsDataUpdatedEvent - when: 'dataUpdatedEvent is received' - def future = objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp) - and: 'wait for async processing to complete' - future.get(10, TimeUnit.SECONDS) - then: 'async process completed successfully' - future.isDone() - and: 'notification is sent' - expectedSendNotificationCount * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) - where: - scenario | dataspaceName || expectedSendNotificationCount - 'dataspace name does not match filter' | 'does-not-match-pattern' || 0 - 'dataspace name matches filter' | 'my-dataspace-published' || 1 - } - - def '#scenario are changed with xpath #xpath and operation #operation'() { - given: 'notification is enabled' - spyNotificationProperties.isEnabled() >> true - and: 'event factory creates event if operation is #operation' - def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, expectedOperationInEvent) >> - cpsDataUpdatedEvent - when: 'dataUpdatedEvent is received for #xpath' - def future = objectUnderTest.processDataUpdatedEvent(anchor, xpath, operation, myObservedTimestamp) - and: 'wait for async processing to complete' - future.get(10, TimeUnit.SECONDS) - then: 'async process completed successfully' - future.isDone() - and: 'notification is sent' - 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) - where: - scenario | xpath | operation || expectedOperationInEvent - 'Same event is sent when root nodes' | '' | Operation.CREATE || Operation.CREATE - 'Same event is sent when root nodes' | '' | Operation.UPDATE || Operation.UPDATE - 'Same event is sent when root nodes' | '' | Operation.DELETE || Operation.DELETE - 'Same event is sent when root nodes' | '/' | Operation.CREATE || Operation.CREATE - 'Same event is sent when root nodes' | '/' | Operation.UPDATE || Operation.UPDATE - 'Same event is sent when root nodes' | '/' | Operation.DELETE || Operation.DELETE - 'Same event is sent when container nodes' | '/parent' | Operation.CREATE || Operation.CREATE - 'Same event is sent when container nodes' | '/parent' | Operation.UPDATE || Operation.UPDATE - 'Same event is sent when container nodes' | '/parent' | Operation.DELETE || Operation.DELETE - 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.CREATE || Operation.UPDATE - 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.UPDATE || Operation.UPDATE - 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.DELETE || Operation.UPDATE - } - - def 'Error handling in notification service.'() { - given: 'notification is enabled' - spyNotificationProperties.isEnabled() >> true - and: 'event factory can not create event successfully' - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >> - { throw new Exception("Could not create event") } - when: 'event is sent for processing' - def future = objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp) - and: 'wait for async processing to complete' - future.get(10, TimeUnit.SECONDS) - then: 'async process completed successfully' - future.isDone() - and: 'error is handled and not thrown to caller' - notThrown Exception - 1 * spyNotificationErrorHandler.onException(_, _, _, '/', Operation.CREATE) - } - - def 'Disabled Notification services'() { - given: 'a notification service that is disabled' - spyNotificationProperties.enabled >> false - NotificationService notificationService = new NotificationService(spyNotificationProperties, mockNotificationPublisher, mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler, mockCpsAdminService) - notificationService.init() - expect: 'it will not send notifications' - assert notificationService.shouldSendNotification('') == false - } -} diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy index ff6ab346f2..b975de6555 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy @@ -24,7 +24,7 @@ package org.onap.cps.utils import com.hazelcast.map.IMap import org.onap.cps.TestUtils -import org.onap.cps.api.CpsAdminService +import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.impl.YangTextSchemaSourceSetCache import org.onap.cps.cache.AnchorDataCacheEntry import org.onap.cps.spi.model.Anchor @@ -34,13 +34,13 @@ import spock.lang.Specification class PrefixResolverSpec extends Specification { - def mockCpsAdminService = Mock(CpsAdminService) + def mockCpsAnchorService = Mock(CpsAnchorService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) def mockAnchorDataCache = Mock(IMap<String, AnchorDataCacheEntry>) - def objectUnderTest = new PrefixResolver(mockCpsAdminService, mockYangTextSchemaSourceSetCache, mockAnchorDataCache) + def objectUnderTest = new PrefixResolver(mockCpsAnchorService, mockYangTextSchemaSourceSetCache, mockAnchorDataCache) def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet) @@ -52,7 +52,7 @@ class PrefixResolverSpec extends Specification { given: 'an anchor for the test-tree model' def anchor = new Anchor(dataspaceName: 'testDataspace', name: 'testAnchor') and: 'the system can get this anchor' - mockCpsAdminService.getAnchor('testDataspace', 'testAnchor') >> anchor + mockCpsAnchorService.getAnchor('testDataspace', 'testAnchor') >> anchor and: 'the schema source cache contains the schema context for the test-tree module' mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext } |