diff options
Diffstat (limited to 'cps-ncmp-service/src')
15 files changed, 216 insertions, 110 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java index 4230140d26..20545d711d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java @@ -29,6 +29,7 @@ import org.onap.cps.ncmp.api.impl.inventory.CompositeState; import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.models.CmResourceAddress; import org.onap.cps.ncmp.api.models.DataOperationRequest; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; @@ -53,18 +54,14 @@ public interface NetworkCmProxyDataService { /** * Get resource data for given data store using dmi. * - * @param datastoreName datastore name - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier + * @param cmResourceAddress target datastore, cm handle and resource identifier * @param optionsParamInQuery options query * @param topicParamInQuery topic name for (triggering) async responses * @param requestId unique requestId for async request * @param authorization contents of Authorization header, or null if not present * @return {@code Object} resource data */ - Object getResourceDataForCmHandle(String datastoreName, - String cmHandleId, - String resourceIdentifier, + Object getResourceDataForCmHandle(CmResourceAddress cmResourceAddress, String optionsParamInQuery, String topicParamInQuery, String requestId, @@ -73,15 +70,11 @@ public interface NetworkCmProxyDataService { /** * Get resource data for operational. * - * @param datastoreName datastore name - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier + * @param cmResourceAddress target datastore, cm handle and resource identifier * @Link FetchDescendantsOption fetch descendants option * @return {@code Object} resource data */ - Object getResourceDataForCmHandle(String datastoreName, - String cmHandleId, - String resourceIdentifier, + Object getResourceDataForCmHandle(CmResourceAddress cmResourceAddress, FetchDescendantsOption fetchDescendantsOption); /** @@ -110,11 +103,11 @@ public interface NetworkCmProxyDataService { * @return {@code Object} return data */ Object writeResourceDataPassThroughRunningForCmHandle(String cmHandleId, - String resourceIdentifier, - OperationType operationType, - String requestBody, - String contentType, - String authorization); + String resourceIdentifier, + OperationType operationType, + String requestBody, + String contentType, + String authorization); /** * Retrieve module references for the given cm handle. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index 6ab6eab653..c15df9c869 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -72,6 +72,7 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; +import org.onap.cps.ncmp.api.models.CmResourceAddress; import org.onap.cps.ncmp.api.models.DataOperationRequest; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; @@ -127,15 +128,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } @Override - public Object getResourceDataForCmHandle(final String datastoreName, - final String cmHandleId, - final String resourceIdentifier, + public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, final String optionsParamInQuery, final String topicParamInQuery, final String requestId, final String authorization) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(datastoreName, cmHandleId, - resourceIdentifier, + final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, optionsParamInQuery, topicParamInQuery, requestId, @@ -144,12 +142,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } @Override - public Object getResourceDataForCmHandle(final String datastoreName, - final String cmHandleId, - final String resourceIdentifier, + public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataService.getDataNodes(datastoreName, cmHandleId, resourceIdentifier, - fetchDescendantsOption).iterator().next(); + return cpsDataService.getDataNodes(cmResourceAddress.datastoreName(), + cmResourceAddress.cmHandleId(), + cmResourceAddress.resourceIdentifier(), + fetchDescendantsOption).iterator().next(); } @Override diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java index 38f3db98de..6b02adb654 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java @@ -57,4 +57,15 @@ public interface CmNotificationSubscriptionPersistenceService { */ Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType, final String cmHandleId, final String xpath); + + /** + * Add or update cm notification subscription. + * + * @param datastoreType valid datastore type + * @param cmHandle cmhandle id + * @param xpath valid xpath + * @param newSubscriptionId subscription Id to be added + */ + void addOrUpdateCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandle, + final String xpath, final String newSubscriptionId); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java index 6e4997a4dd..2efd321b8d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java @@ -20,15 +20,25 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.service; +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; + +import java.io.Serializable; +import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsQueryService; +import org.onap.cps.cpspath.parser.CpsPathUtil; import org.onap.cps.ncmp.api.impl.operations.DatastoreType; -import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; +import org.onap.cps.utils.ContentType; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; @Slf4j @@ -37,14 +47,16 @@ import org.springframework.stereotype.Service; public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotificationSubscriptionPersistenceService { private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions"; - private static final String IS_ONGOING_CM_SUBSCRIPTION_CPS_PATH_QUERY = """ + private static final String CM_SUBSCRIPTION_CPS_PATH_QUERY = """ /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters/filter[@xpath='%s'] """.trim(); private static final String SUBSCRIPTION_IDS_CPS_PATH_QUERY = """ //filter/subscriptionIds[text()='%s'] """.trim(); + private final JsonObjectMapper jsonObjectMapper; private final CpsQueryService cpsQueryService; + private final CpsDataService cpsDataService; @Override public boolean isOngoingCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, @@ -56,7 +68,7 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif public boolean isUniqueSubscriptionId(final String subscriptionId) { return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted(subscriptionId), - FetchDescendantsOption.OMIT_DESCENDANTS).isEmpty(); + OMIT_DESCENDANTS).isEmpty(); } @Override @@ -64,17 +76,59 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif final String cmHandleId, final String xpath) { final String isOngoingCmSubscriptionCpsPathQuery = - IS_ONGOING_CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, + CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)); final Collection<DataNode> existingNodes = cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, - isOngoingCmSubscriptionCpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS); + isOngoingCmSubscriptionCpsPathQuery, OMIT_DESCENDANTS); if (existingNodes.isEmpty()) { return Collections.emptyList(); } return (List<String>) existingNodes.iterator().next().getLeaves().get("subscriptionIds"); } + @Override + public void addOrUpdateCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, + final String xpath, final String newSubscriptionId) { + if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)) { + final DataNode existingFilterNode = + cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, + CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, + escapeQuotesByDoublingThem(xpath)), + OMIT_DESCENDANTS).iterator().next(); + final Collection<String> existingSubscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType, + cmHandleId, xpath); + if (!existingSubscriptionIds.contains(newSubscriptionId)) { + updateListOfSubscribers(existingSubscriptionIds, newSubscriptionId, existingFilterNode); + } + } else { + addNewSubscriptionViaDatastore(datastoreType, cmHandleId, xpath, newSubscriptionId); + } + } + + private void addNewSubscriptionViaDatastore(final DatastoreType datastoreType, final String cmHandleId, + final String xpath, final String newSubscriptionId) { + final String parentXpath = "/datastores/datastore[@name='%s']/cm-handles" + .formatted(datastoreType.getDatastoreName()); + final String updatedJson = String.format("{\"cm-handle\":[{\"id\":\"%s\",\"filters\":{\"filter\":" + + "[{\"xpath\":\"%s\",\"subscriptionIds\":[\"%s\"]}]}}]}", cmHandleId, xpath, newSubscriptionId); + cpsDataService.saveData(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, parentXpath, updatedJson, + OffsetDateTime.now(), ContentType.JSON); + } + + private void updateListOfSubscribers(final Collection<String> existingSubscriptionIds, + final String newSubscriptionId, final DataNode existingFilterNode) { + final String parentXpath = CpsPathUtil.getNormalizedParentXpath(existingFilterNode.getXpath()); + final List<String> updatedSubscribers = new ArrayList<>(existingSubscriptionIds); + updatedSubscribers.add(newSubscriptionId); + final Map<String, Serializable> updatedLeaves = new HashMap<>(); + updatedLeaves.put("xpath", existingFilterNode.getLeaves().get("xpath")); + updatedLeaves.put("subscriptionIds", (Serializable) updatedSubscribers); + final String updatedJson = "{\"filter\":[" + jsonObjectMapper.asJsonString(updatedLeaves) + "]}"; + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, parentXpath, updatedJson, + OffsetDateTime.now()); + } + private static String escapeQuotesByDoublingThem(final String inputXpath) { return inputXpath.replace("'", "''"); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 2a4bceca23..a9ec1241bc 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -42,6 +42,7 @@ import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; import org.onap.cps.ncmp.api.impl.utils.data.operation.ResourceDataOperationRequestUtils; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.CmResourceAddress; import org.onap.cps.ncmp.api.models.DataOperationRequest; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.utils.JsonObjectMapper; @@ -70,9 +71,7 @@ public class DmiDataOperations extends DmiOperations { * This method fetches the resource data from operational data store for given cm handle * identifier on given resource using dmi client. * - * @param dataStoreName name of data store - * @param cmHandleId network resource identifier - * @param resourceId resource identifier + * @param cmResourceAddress target datastore, cm handle and resource identifier * @param optionsParamInQuery options query * @param topicParamInQuery topic name for (triggering) async responses * @param requestId requestId for async responses @@ -82,19 +81,17 @@ public class DmiDataOperations extends DmiOperations { @Timed(value = "cps.ncmp.dmi.get", description = "Time taken to fetch the resource data from operational data store for given cm handle " + "identifier on given resource using dmi client") - public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, - final String cmHandleId, - final String resourceId, + public ResponseEntity<Object> getResourceDataFromDmi(final CmResourceAddress cmResourceAddress, final String optionsParamInQuery, final String topicParamInQuery, final String requestId, final String authorization) { - final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.cmHandleId()); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); - final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, - yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery, + final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); + final String dmiResourceDataUrl = getDmiRequestUrl(cmResourceAddress.datastoreName(), + cmResourceAddress.cmHandleId(), cmResourceAddress.resourceIdentifier(), optionsParamInQuery, topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ, authorization); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java index f14439f690..60f39fcea0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java @@ -115,23 +115,7 @@ public class AlternateIdChecker { for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) { final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId(); - final boolean isAcceptable; - if (StringUtils.isEmpty(proposedAlternateId)) { - isAcceptable = true; - } else { - if (acceptedAlternateIds.contains(proposedAlternateId)) { - isAcceptable = false; - log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " - + "assigned to a different cm handle (in this batch)", cmHandleId); - } else { - if (Operation.CREATE.equals(operation)) { - isAcceptable = canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId); - } else { - isAcceptable = canApplyAlternateId(cmHandleId, proposedAlternateId); - } - } - } - if (isAcceptable) { + if (isProposedAlternateIdAcceptable(proposedAlternateId, operation, acceptedAlternateIds, cmHandleId)) { acceptedAlternateIds.add(proposedAlternateId); } else { rejectedCmHandleIds.add(cmHandleId); @@ -140,6 +124,22 @@ public class AlternateIdChecker { return rejectedCmHandleIds; } + private boolean isProposedAlternateIdAcceptable(final String proposedAlternateId, final Operation operation, + final Set<String> acceptedAlternateIds, final String cmHandleId) { + if (StringUtils.isEmpty(proposedAlternateId)) { + return true; + } + if (acceptedAlternateIds.contains(proposedAlternateId)) { + log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " + + "assigned to a different cm handle (in this batch)", cmHandleId); + return false; + } + if (Operation.CREATE.equals(operation)) { + return canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId); + } + return canApplyAlternateId(cmHandleId, proposedAlternateId); + } + private boolean alternateIdAlreadyInDb(final String alternateId) { try { inventoryPersistence.getCmHandleDataNodeByAlternateId(alternateId); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmResourceAddress.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmResourceAddress.java new file mode 100644 index 0000000000..21d82fcf56 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmResourceAddress.java @@ -0,0 +1,25 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.models; + +public record CmResourceAddress(String datastoreName, String cmHandleId, String resourceIdentifier) { + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java index a3ebec3c54..4cc8cdaa66 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java @@ -70,24 +70,6 @@ abstract class AbstractModelLoader implements ModelLoader { } } - void waitUntilDataspaceIsAvailable(final String dataspaceName) { - log.info("Model Loader start-up, waiting for database to be ready"); - int attemptCount = 0; - while (cpsDataspaceService.getDataspace(dataspaceName) == null) { - if (attemptCount < maximumAttemptCount) { - try { - Thread.sleep(attemptCount * retryTimeMs); - log.info("Retrieving dataspace {} ... {} attempt(s) ", dataspaceName, ++attemptCount); - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - } - } else { - throw new NcmpStartUpException("Retrieval of NCMP dataspace failed", - dataspaceName + " not available (yet)"); - } - } - } - void createSchemaSet(final String dataspaceName, final String schemaSetName, final String... resourceNames) { try { final Map<String, String> yangResourcesContentMap = createYangResourcesToContentMap(resourceNames); @@ -100,6 +82,17 @@ abstract class AbstractModelLoader implements ModelLoader { } } + void createDataspace(final String dataspaceName) { + try { + cpsDataspaceService.createDataspace(dataspaceName); + } catch (final AlreadyDefinedException alreadyDefinedException) { + log.debug("Dataspace already exists"); + } catch (final Exception exception) { + log.error("Creating dataspace failed: {} ", exception.getMessage()); + throw new NcmpStartUpException("Creating dataspace failed", exception.getMessage()); + } + } + void deleteUnusedSchemaSets(final String dataspaceName, final String... schemaSetNames) { for (final String schemaSetName : schemaSetNames) { try { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java index 4c31719a29..a0b7bd5826 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java @@ -51,12 +51,12 @@ public class CmDataSubscriptionModelLoader extends AbstractModelLoader { @Override public void onboardOrUpgradeModel() { - waitUntilDataspaceIsAvailable(NCMP_DATASPACE_NAME); onboardSubscriptionModels(); log.info("Subscription Models onboarded successfully"); } private void onboardSubscriptionModels() { + createDataspace(NCMP_DATASPACE_NAME); createSchemaSet(NCMP_DATASPACE_NAME, SCHEMASET_NAME, MODEL_FILENAME); createAnchor(NCMP_DATASPACE_NAME, SCHEMASET_NAME, ANCHOR_NAME); createTopLevelDataNode(NCMP_DATASPACE_NAME, ANCHOR_NAME, REGISTRY_DATANODE_NAME); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java index d0d63ab8eb..7c25953f0d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java @@ -22,6 +22,7 @@ package org.onap.cps.ncmp.init; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAnchorService; @@ -33,9 +34,9 @@ import org.springframework.stereotype.Service; @Slf4j @Service public class InventoryModelLoader extends AbstractModelLoader { - private static final String NEW_MODEL_FILE_NAME = "dmi-registry@2024-02-23.yang"; private static final String NEW_SCHEMA_SET_NAME = "dmi-registry-2024-02-23"; + private static final String REGISTRY_DATANODE_NAME = "dmi-registry"; public InventoryModelLoader(final CpsDataspaceService cpsDataspaceService, final CpsModuleService cpsModuleService, @@ -46,20 +47,24 @@ public class InventoryModelLoader extends AbstractModelLoader { @Override public void onboardOrUpgradeModel() { - waitUntilDataspaceIsAvailable(NCMP_DATASPACE_NAME); updateInventoryModel(); log.info("Inventory Model updated successfully"); } private void updateInventoryModel() { + createDataspace(NCMP_DATASPACE_NAME); + createDataspace(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME); createSchemaSet(NCMP_DATASPACE_NAME, NEW_SCHEMA_SET_NAME, NEW_MODEL_FILE_NAME); + createAnchor(NCMP_DATASPACE_NAME, NEW_SCHEMA_SET_NAME, NCMP_DMI_REGISTRY_ANCHOR); updateAnchorSchemaSet(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NEW_SCHEMA_SET_NAME); + createTopLevelDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, REGISTRY_DATANODE_NAME); deleteOldButNotThePreviousSchemaSets(); } private void deleteOldButNotThePreviousSchemaSets() { //No schema sets passed in yet, but wil be required for future updates deleteUnusedSchemaSets(NCMP_DATASPACE_NAME); + deleteUnusedSchemaSets(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME); } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 74016e4c0c..d47be6cd5c 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -24,6 +24,7 @@ package org.onap.cps.ncmp.api.impl import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse +import org.onap.cps.ncmp.api.models.CmResourceAddress import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME @@ -121,35 +122,27 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { >> { new ResponseEntity<>(HttpStatus.CREATED) } } - def 'Get resource data for pass-through operational from DMI.'() { + def 'Get resource data for from DMI.'() { given: 'cpsDataService returns valid data node' mockDataNode() + and: 'some cm resource address' + def cmResourceAddress = new CmResourceAddress('some datastore','some CM Handle', 'some resource Id') and: 'get resource data from DMI is called' - mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.datastoreName,'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >> + mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >> new ResponseEntity<>('dmi-response', HttpStatus.OK) - when: 'get resource data operational for cm-handle is called' - def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) + when: 'get resource data operational for the given cm resource address is called' + def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) then: 'DMI returns a json response' assert response == 'dmi-response' } - def 'Get resource data for pass-through running from DMI.'() { - given: 'cpsDataService returns valid data node' - mockDataNode() - and: 'DMI returns valid response and data' - mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >> - new ResponseEntity<>('{dmi-response}', HttpStatus.OK) - when: 'get resource data is called' - def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) - then: 'get resource data returns expected response' - assert response == '{dmi-response}' - } - def 'Get resource data for operational (cached) data.'() { given: 'CPS Data service returns some object(s)' mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object'] + and: 'a cm resource address for the same datastore, cm handle and resource id' + def cmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId') when: 'get resource data is called' - def response = objectUnderTest.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) + def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, FetchDescendantsOption.OMIT_DESCENDANTS) then: 'get resource data returns teh first object from the data service' assert response == 'First Object' } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy index eb0e1100ed..19ebc3d711 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy @@ -20,18 +20,22 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.service - +import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsQueryService import org.onap.cps.ncmp.api.impl.operations.DatastoreType import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.DataNode +import org.onap.cps.utils.JsonObjectMapper +import com.fasterxml.jackson.databind.ObjectMapper import spock.lang.Specification class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification { + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) def mockCpsQueryService = Mock(CpsQueryService) + def mockCpsDataService = Mock(CpsDataService) - def objectUnderTest = new CmNotificationSubscriptionPersistenceServiceImpl(mockCpsQueryService) + def objectUnderTest = new CmNotificationSubscriptionPersistenceServiceImpl(jsonObjectMapper, mockCpsQueryService, mockCpsDataService) def 'Check ongoing cm subscription #scenario'() { given: 'a valid cm subscription query' @@ -64,4 +68,43 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification 'datanodes present' | [new DataNode()] || false 'no datanodes present' | [] || true } + + def 'Add new subscriber to an ongoing cm notification subscription'() { + given: 'a valid cm subscription path query' + def cpsPathQuery = objectUnderTest.CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y'); + and: 'a dataNode exists for the given cps path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])] + when: 'the method to add/update cm notification subscription is called' + objectUnderTest.addOrUpdateCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1','/x/y', 'newSubId') + then: 'data service method to update list of subscribers is called once' + 1 * mockCpsDataService.updateNodeLeaves( + 'NCMP-Admin', + 'cm-data-subscriptions', + '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', + '{"filter":[{"xpath":"/x/y","subscriptionIds":["sub-1","newSubId"]}]}', _) + } + + def 'Add new cm notification subscription for #datastoreType'() { + given: 'a valid cm subscription path query' + def cpsPathQuery = objectUnderTest.CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreName, 'ch-1', '/x/y') + and: 'a parent node xpath for given path above' + def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles' + and: 'a datanode does not exist for the given cps path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cpsPathQuery.formatted(datastoreName), + FetchDescendantsOption.OMIT_DESCENDANTS) >> [] + when: 'the method to add/update cm notification subscription is called' + objectUnderTest.addOrUpdateCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId') + then: 'data service method to update list of subscribers is called once with the correct parameters' + 1 * mockCpsDataService.saveData( + 'NCMP-Admin', + 'cm-data-subscriptions', + parentNodeXpath.formatted(datastoreName), + '{"cm-handle":[{"id":"ch-1","filters":{"filter":[{"xpath":"/x/y","subscriptionIds":["newSubId"]}]}}]}', _,_) + where: + scenario | datastoreType || datastoreName + 'passthrough_running' | DatastoreType.PASSTHROUGH_RUNNING || "ncmp-datastore:passthrough-running" + 'passthrough_operational' | DatastoreType.PASSTHROUGH_OPERATIONAL || "ncmp-datastore:passthrough-operational" + } }
\ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy index e154588a10..eb6c7a0f48 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy @@ -28,6 +28,7 @@ import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext import org.onap.cps.ncmp.api.models.DataOperationRequest +import org.onap.cps.ncmp.api.models.CmResourceAddress import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent import org.onap.cps.ncmp.utils.TestUtils import org.onap.cps.utils.JsonObjectMapper @@ -81,8 +82,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl when: 'get resource data is invoked' - def result = objectUnderTest.getResourceDataFromDmi(dataStore.datastoreName, cmHandleId, resourceIdentifier, - options, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) + def cmResourceAddress = new CmResourceAddress(dataStore.datastoreName, cmHandleId, resourceIdentifier) + def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, options, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) then: 'the result is the response from the DMI service' assert result == responseFromDmi where: 'the following parameters are used' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy index 0eabaa1d28..1e843676be 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy @@ -80,7 +80,8 @@ class AlternateIdCheckerSpec extends Specification { assert result == expectedRejectedCmHandleIds where: 'the following alternate ids are used' scenario | alt1 | alt2 | altAlreadyInDb || expectedRejectedCmHandleIds - 'no alternate ids' | '' | '' | ['dont matter'] || [] + 'blank alternate ids' | '' | '' | ['dont matter'] || [] + 'null alternate ids' | null | null | ['dont matter'] || [] 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || [] 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || ['ch-1'] 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || ['ch-2'] diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy index f122b57634..b0be29d933 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy @@ -81,14 +81,6 @@ class AbstractModelLoaderSpec extends Specification { assert logs.contains('test message') } - def 'Wait for non-existing dataspace'() { - when: 'wait for the dataspace' - objectUnderTest.waitUntilDataspaceIsAvailable('some dataspace') - then: 'a startup exception is thrown' - def thrown = thrown(NcmpStartUpException) - assert thrown.message.contains('Retrieval of NCMP dataspace failed') - } - def 'Create schema set.'() { when: 'creating a schema set' objectUnderTest.createSchemaSet('some dataspace','new name','cm-data-subscriptions@2024-02-12.yang') |