Error reporting when registering cm handle with alternate id 39/137239/9
authorhalil.cakal <halil.cakal@est.tech>
Mon, 19 Feb 2024 21:37:25 +0000 (21:37 +0000)
committerHalil Cakal <halil.cakal@est.tech>
Fri, 23 Feb 2024 10:31:16 +0000 (10:31 +0000)
- refactored registration code all use cases; 1 method for each action
- introduce new cps error code; 111

- TODO: error reporting registration, UPDATE

Issue-ID: CPS-2100

Change-Id: I5049777ee4e08fdc94aa1db09e668e952ed8e1c3
Signed-off-by: halil.cakal <halil.cakal@est.tech>
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
docs/cps-ncmp-message-status-codes.rst

index b9c834c..462679e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-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.
@@ -36,7 +36,8 @@ public enum NcmpResponseStatus {
     SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"),
     UNKNOWN_ERROR("108", "Unknown error"),
     CM_HANDLE_ALREADY_EXIST("109", "cm-handle already exists"),
-    CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id");
+    CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"),
+    ALTERNATE_ID_ALREADY_ASSOCIATED("111", "alternate id already associated");
 
     private final String code;
     private final String message;
index 1f2748b..08afde4 100755 (executable)
@@ -24,6 +24,7 @@
 
 package org.onap.cps.ncmp.api.impl;
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED;
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND;
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY;
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST;
@@ -306,45 +307,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState();
     }
 
-    /**
-     * THis method registers a cm handle and initiates modules sync.
-     *
-     * @param dmiPluginRegistration dmi plugin registration information.
-     * @return cm-handle registration response for create cm-handle requests.
-     */
-    public List<CmHandleRegistrationResponse> parseAndProcessCreatedCmHandlesInRegistration(
-        final DmiPluginRegistration dmiPluginRegistration, final Collection<String> acceptedCmHandleIds) {
-        final List<NcmpServiceCmHandle> cmHandlesToBeCreated = dmiPluginRegistration.getCreatedCmHandles();
-        final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size());
-        final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(cmHandlesToBeCreated.size());
-        cmHandlesToBeCreated
-                .forEach(cmHandle -> {
-                    if (acceptedCmHandleIds.contains(cmHandle.getCmHandleId())) {
-                        final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(
-                                dmiPluginRegistration.getDmiPlugin(),
-                                dmiPluginRegistration.getDmiDataPlugin(),
-                                dmiPluginRegistration.getDmiModelPlugin(),
-                                cmHandle,
-                                cmHandle.getModuleSetTag(),
-                                cmHandle.getAlternateId());
-                        yangModelCmHandles.add(yangModelCmHandle);
-                        initialTrustLevelPerCmHandleId.put(cmHandle.getCmHandleId(),
-                            cmHandle.getRegistrationTrustLevel());
-                    }
-                });
-        return registerNewCmHandles(yangModelCmHandles, initialTrustLevelPerCmHandleId);
-    }
-
-    protected List<CmHandleRegistrationResponse> parseAndProcessDeletedCmHandlesInRegistration(
-        final List<String> tobeRemovedCmHandles) {
+    protected void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
+                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
+        final List<String> tobeRemovedCmHandleIds = dmiPluginRegistration.getRemovedCmHandles();
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
-            new ArrayList<>(tobeRemovedCmHandles.size());
+            new ArrayList<>(tobeRemovedCmHandleIds.size());
         final Collection<YangModelCmHandle> yangModelCmHandles =
-            inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles);
+            inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandleIds);
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING);
 
         final Set<String> notDeletedCmHandles = new HashSet<>();
-        for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandles, DELETE_BATCH_SIZE)) {
+        for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, DELETE_BATCH_SIZE)) {
             try {
                 batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch);
                 tobeRemovedCmHandleBatch.forEach(cmHandleId ->
@@ -362,60 +335,58 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
                 }
             }
         }
-
         yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId()));
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED);
-
-        return cmHandleRegistrationResponses;
+        dmiPluginRegistrationResponse.setRemovedCmHandles(cmHandleRegistrationResponses);
     }
 
-    private void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
+    protected void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
                                          final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
-        if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setRemovedCmHandles(
-                parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles()));
-        }
-    }
+        final List<NcmpServiceCmHandle> ncmpServiceCmHandles = dmiPluginRegistration.getCreatedCmHandles();
+        final List<CmHandleRegistrationResponse> failedCmHandleRegistrationResponses = new ArrayList<>();
 
-    private void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
-                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
-        final Collection<String> acceptedCmHandleIds = alternateIdChecker
-            .getIdsOfCmHandlesWithAcceptableAlternateId(dmiPluginRegistration.getCreatedCmHandles());
-        if (!acceptedCmHandleIds.isEmpty()) {
-            dmiPluginRegistrationResponse.setCreatedCmHandles(
-                parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration, acceptedCmHandleIds));
-        }
-    }
+        try {
+            final Collection<String> rejectedCmHandleIds
+                = checkAlternateIds(ncmpServiceCmHandles, failedCmHandleRegistrationResponses);
 
-    private void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
-                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
-        if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setUpdatedCmHandles(
-                networkCmProxyDataServicePropertyHandler
-                    .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
+            final Collection<String> succeededCmHandleIds = persistCmHandlesWithState(dmiPluginRegistration,
+                dmiPluginRegistrationResponse, ncmpServiceCmHandles, rejectedCmHandleIds);
+
+            processTrustLevels(ncmpServiceCmHandles, succeededCmHandleIds);
+
+        } catch (final AlreadyDefinedException alreadyDefinedException) {
+            failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponsesFromXpaths(
+                alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST));
+        } catch (final Exception exception) {
+            final Collection<String> cmHandleIds =
+                ncmpServiceCmHandles.stream().map(NcmpServiceCmHandle::getCmHandleId).collect(Collectors.toList());
+            failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse
+                .createFailureResponses(cmHandleIds, exception));
         }
+        final List<CmHandleRegistrationResponse> mergedCmHandleRegistrationResponses
+            = new ArrayList<>(failedCmHandleRegistrationResponses);
+        mergedCmHandleRegistrationResponses.addAll(dmiPluginRegistrationResponse.getCreatedCmHandles());
+
+        dmiPluginRegistrationResponse.setCreatedCmHandles(mergedCmHandleRegistrationResponses);
     }
 
-    private void processUpgradedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
-                                          final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
-        if (dmiPluginRegistration.getUpgradedCmHandles() != null
-            && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setUpgradedCmHandles(
-                parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration));
-        }
+    protected void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
+                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
+        dmiPluginRegistrationResponse.setUpdatedCmHandles(networkCmProxyDataServicePropertyHandler
+            .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
     }
 
+    protected void processUpgradedCmHandles(
+        final DmiPluginRegistration dmiPluginRegistration,
+        final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
 
-    protected List<CmHandleRegistrationResponse> parseAndProcessUpgradedCmHandlesInRegistration(
-        final DmiPluginRegistration dmiPluginRegistration) {
-
-        final List<String> upgradedCmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles();
+        final List<String> cmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles();
         final String upgradedModuleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag();
         final Map<YangModelCmHandle, CmHandleState> acceptedCmHandleStatePerCmHandle
-                = new HashMap<>(upgradedCmHandleIds.size());
-        final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses = new ArrayList<>(upgradedCmHandleIds.size());
+            = new HashMap<>(cmHandleIds.size());
+        final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses = new ArrayList<>(cmHandleIds.size());
 
-        for (final String cmHandleId : upgradedCmHandleIds) {
+        for (final String cmHandleId : cmHandleIds) {
             try {
                 final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
                 if (yangModelCmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) {
@@ -442,7 +413,61 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             }
         }
         cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle));
-        return cmHandleUpgradeResponses;
+        dmiPluginRegistrationResponse.setUpgradedCmHandles(cmHandleUpgradeResponses);
+    }
+
+    private Collection<String> checkAlternateIds(
+        final List<NcmpServiceCmHandle> cmHandlesToBeCreated,
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) {
+        final Collection<String> rejectedCmHandleIds = alternateIdChecker
+            .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated);
+        cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(
+            rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED));
+        return rejectedCmHandleIds;
+    }
+
+    private List<String> persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration,
+                                                   final DmiPluginRegistrationResponse dmiPluginRegistrationResponse,
+                                                   final List<NcmpServiceCmHandle> cmHandlesToBeCreated,
+                                                   final Collection<String> rejectedCmHandleIds) {
+        final List<String> succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size());
+        final List<YangModelCmHandle> yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size());
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
+            new ArrayList<>(cmHandlesToBeCreated.size());
+        for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) {
+            if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) {
+                yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle));
+                cmHandleRegistrationResponses.add(
+                    CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId()));
+                succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId());
+            }
+        }
+        lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister);
+        dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses);
+        return succeededCmHandleIds;
+    }
+
+    private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration,
+                                                   final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        return YangModelCmHandle.toYangModelCmHandle(
+            dmiPluginRegistration.getDmiPlugin(),
+            dmiPluginRegistration.getDmiDataPlugin(),
+            dmiPluginRegistration.getDmiModelPlugin(),
+            ncmpServiceCmHandle,
+            ncmpServiceCmHandle.getModuleSetTag(),
+            ncmpServiceCmHandle.getAlternateId());
+    }
+
+    private void processTrustLevels(final Collection<NcmpServiceCmHandle> cmHandlesToBeCreated,
+                                    final Collection<String> succeededCmHandleIds) {
+        final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size());
+        for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) {
+            if (succeededCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) {
+                initialTrustLevelPerCmHandleId.put(ncmpServiceCmHandle.getCmHandleId(),
+                    ncmpServiceCmHandle.getRegistrationTrustLevel());
+            }
+        }
+        trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId);
     }
 
     private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle,
@@ -488,15 +513,14 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) {
         inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId);
-        inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId
-            + "']");
+        inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']");
         removeDeletedCmHandleFromModuleSyncMap(cmHandleId);
     }
 
-    private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> tobeRemovedCmHandles) {
-        inventoryPersistence.deleteSchemaSetsWithCascade(tobeRemovedCmHandles);
-        inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(tobeRemovedCmHandles));
-        tobeRemovedCmHandles.forEach(this::removeDeletedCmHandleFromModuleSyncMap);
+    private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> cmHandleIds) {
+        inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds);
+        inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds));
+        cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap);
     }
 
     private Collection<String> mapCmHandleIdsToXpaths(final Collection<String> cmHandles) {
@@ -506,25 +530,9 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     }
 
     // CPS-1239 Robustness cleaning of in progress cache
-    private void removeDeletedCmHandleFromModuleSyncMap(final String deletedCmHandleId) {
-        if (moduleSyncStartedOnCmHandles.remove(deletedCmHandleId) != null) {
-            log.debug("{} removed from in progress map", deletedCmHandleId);
-        }
-    }
-
-    private List<CmHandleRegistrationResponse> registerNewCmHandles(final List<YangModelCmHandle> yangModelCmHandles,
-                                                                    final Map<String, TrustLevel>
-                                                                            initialTrustLevelPerCmHandleId) {
-        final Set<String> cmHandleIds = initialTrustLevelPerCmHandleId.keySet();
-        try {
-            lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandles);
-            trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId);
-            return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds);
-        } catch (final AlreadyDefinedException alreadyDefinedException) {
-            return CmHandleRegistrationResponse.createFailureResponses(
-                    alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST);
-        } catch (final Exception exception) {
-            return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception);
+    private void removeDeletedCmHandleFromModuleSyncMap(final String cmHandleId) {
+        if (moduleSyncStartedOnCmHandles.remove(cmHandleId) != null) {
+            log.debug("{} removed from in progress map", cmHandleId);
         }
     }
 
@@ -553,6 +561,4 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
-
-
 }
index 1478c86..f5d22af 100644 (file)
@@ -77,7 +77,8 @@ public class NetworkCmProxyDataServicePropertyHandler {
      */
     public List<CmHandleRegistrationResponse> updateCmHandleProperties(
         final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
-        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses
+            = new ArrayList<>(ncmpServiceCmHandles.size());
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
             final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
             try {
index 1be1a90..4ac6537 100644 (file)
@@ -103,10 +103,10 @@ public class AlternateIdChecker {
      * @param newNcmpServiceCmHandles the proposed new cm handles
      * @return collection of cm handles ids which are acceptable
      */
-    public Collection<String> getIdsOfCmHandlesWithAcceptableAlternateId(
+    public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId(
                                     final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles) {
         final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size());
-        final Collection<String> acceptedCmHandleIds = new ArrayList<>(newNcmpServiceCmHandles.size());
+        final Collection<String> rejectedCmHandleIds = new ArrayList<>();
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) {
             final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
             final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId();
@@ -124,10 +124,11 @@ public class AlternateIdChecker {
             }
             if (isAcceptable) {
                 acceptedAlternateIds.add(proposedAlternateId);
-                acceptedCmHandleIds.add(cmHandleId);
+            } else {
+                rejectedCmHandleIds.add(cmHandleId);
             }
         }
-        return acceptedCmHandleIds;
+        return rejectedCmHandleIds;
     }
 
     private boolean alternateIdAlreadyInDb(final String alternateId) {
index 8228322..52b8d69 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
- *  Modifications Copyright (C) 2022-2023 Nordix Foundation
+ *  Modifications Copyright (C) 2022-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.
@@ -76,20 +76,22 @@ public class CmHandleRegistrationResponse {
     }
 
     /**
-     * Creates a failure response based on registration error.
+     * Create a failure response of cm handle registration based on xpath and registration error.
+     * Conditions:
+     * - the xpath should be valid according to the cps path, otherwise xpath is not included in the response.
      *
-     * @param failedXpaths       list of failed Xpaths
-     * @param ncmpResponseStatus enum describing the type of registration error
-     * @return CmHandleRegistrationResponse
+     * @param failedXpaths the failed xpaths
+     * @param ncmpResponseStatus type of the registration error
+     * @return collection of cm handle registration response
      */
-    public static List<CmHandleRegistrationResponse> createFailureResponses(final Collection<String> failedXpaths,
-            final NcmpResponseStatus ncmpResponseStatus) {
+    public static List<CmHandleRegistrationResponse> createFailureResponsesFromXpaths(
+        final Collection<String> failedXpaths, final NcmpResponseStatus ncmpResponseStatus) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size());
         for (final String xpath : failedXpaths) {
             try {
                 final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(xpath);
-                cmHandleRegistrationResponses.add(
-                        CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus));
+                cmHandleRegistrationResponses
+                    .add(CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus));
             } catch (IllegalArgumentException | IllegalStateException e) {
                 log.warn("Unexpected xpath {}", xpath);
             }
@@ -97,6 +99,24 @@ public class CmHandleRegistrationResponse {
         return cmHandleRegistrationResponses;
     }
 
+    /**
+     * Create a failure response of cm handle registration based on cm handle id and registration error.
+     *
+     * @param failedCmHandleIds the failed cm handle ids
+     * @param ncmpResponseStatus type of the registration error
+     * @return collection of cm handle registration response
+     */
+    public static List<CmHandleRegistrationResponse> createFailureResponses(
+            final Collection<String> failedCmHandleIds, final NcmpResponseStatus ncmpResponseStatus) {
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
+            new ArrayList<>(failedCmHandleIds.size());
+        for (final String failedCmHandleId : failedCmHandleIds) {
+            cmHandleRegistrationResponses.add(
+                CmHandleRegistrationResponse.createFailureResponse(failedCmHandleId, ncmpResponseStatus));
+        }
+        return cmHandleRegistrationResponses;
+    }
+
     /**
      * Creates a failure response based on other exception.
      *
index 4615af6..7d6a8e1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation
+ *  Copyright (C) 2021-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.
@@ -50,7 +50,7 @@ public class DmiPluginRegistration {
 
     private List<String> removedCmHandles = Collections.emptyList();
 
-    private UpgradedCmHandles upgradedCmHandles;
+    private UpgradedCmHandles upgradedCmHandles = new UpgradedCmHandles();
 
     /**
      * Validates plugin service names.
index 313d1b4..cb7e1ef 100644 (file)
@@ -27,7 +27,6 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
 
-import java.util.stream.Collectors
 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
@@ -71,12 +70,18 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def trustLevelPerDmiPlugin = [:]
     def mockTrustLevelManager = Mock(TrustLevelManager)
     def mockAlternateIdChecker = Mock(AlternateIdChecker)
-    def objectUnderTest = getObjectUnderTest()
+
+    def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
+        mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
+        stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
+        mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker))
 
     def setup() {
         // always accept all cm handles
-        mockAlternateIdChecker.getIdsOfCmHandlesWithAcceptableAlternateId(_) >>
-            { args -> args[0].stream().map(it -> it.cmHandleId).collect(Collectors.toList()) }
+        mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
+
+        // always can find all cm handles in DB
+        mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } }
     }
 
     def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() {
@@ -94,15 +99,16 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         when: 'registration is processed'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
         then: 'cm-handles are removed first'
-            1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_)
+            1 * objectUnderTest.processRemovedCmHandles(*_)
         and: 'de-registered cm handle entry is removed from in progress map'
             1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
         then: 'cm-handles are created'
-            1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_)
+            1 * objectUnderTest.processCreatedCmHandles(*_)
         then: 'cm-handles are updated'
-            1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_)
+            1 * objectUnderTest.processUpdatedCmHandles(*_)
+            1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> []
         then: 'cm-handles are upgraded'
-            1 * objectUnderTest.parseAndProcessUpgradedCmHandlesInRegistration(*_)
+            1 * objectUnderTest.processUpgradedCmHandles(*_)
     }
 
     def 'DMI Registration upgrade operation with upgrade node state #scenario'() {
@@ -137,29 +143,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'cm handle is invalid' | new DataValidationException('some error message', 'some error details')  || '110'
     }
 
-    def 'DMI Registration: Response from all operations types are in response'() {
-        given: 'a registration with operations of all three types'
-            def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
-            dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
-            dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
-            dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
-        and: 'update cm-handles can be processed successfully'
-            def updateResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-2')]
-            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses
-        and: 'create cm-handles can be processed successfully'
-            def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')]
-            objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) >> createdResponses
-        and: 'delete cm-handles can be processed successfully'
-            def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')]
-            objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) >> removeResponses
-        when: 'registration is processed'
-            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
-        then: 'response has values from all operations'
-            response.removedCmHandles == removeResponses
-            response.createdCmHandles == createdResponses
-            response.updatedCmHandles == updateResponses
-    }
-
     def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() {
         given: 'a registration '
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
@@ -168,7 +151,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         when: 'update registration and sync module is called with correct DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'create cm handles registration and sync modules is called with the correct plugin information'
-            1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration, _)
+            1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _)
         and: 'dmi is added to the dmi trustLevel map'
             assert trustLevelPerDmiPlugin.size() == 1
             assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName)
@@ -190,7 +173,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def exceptionThrown = thrown(DmiRequestException.class)
             assert exceptionThrown.getMessage().contains(expectedMessageDetails)
         and: 'registration is not called'
-            0 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)
+            0 * objectUnderTest.processCreatedCmHandles(*_)
         where:
             scenario                         | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
             'empty DMI plugins'              | ''         | ''             | ''            || 'No DMI plugin service names'
@@ -231,22 +214,18 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'without dmi & public properties' | [:]                      | [:]                            || [:]                                        | [:]
     }
 
-    def 'Add CM-Handle to trustLevelPerCmHandle Successfully with: #scenario.'() {
-        given: 'a registration with trustLevel and populated cache'
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
-            dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: TrustLevel.NONE),
-                                                      new NcmpServiceCmHandle(cmHandleId: cmHandleId, registrationTrustLevel: registrationTrustLevel)]
+    def 'Add CM-Handle #scenario.'() {
+        given: ' registration details for one cm handles'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                createdCmHandles:[new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: registrationTrustLevel)])
         when: 'registration is updated'
-            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'a successful response is received'
-            assert response.createdCmHandles.size() == expectedNumberOfCreatedCmHandles
-        and: 'trustLevel is set for the created cm-handle'
-            1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(_)
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'trustLevel is set for the created cm-handle'
+            1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping)
         where:
-            scenario                                 | cmHandleId | registrationTrustLevel || expectedNumberOfCreatedCmHandles
-            'new trusted cm handle'                  | 'ch-new'   | TrustLevel.COMPLETE    || 2
-            'existing cm handle without trust level' | 'ch-1'     | null                   || 1
-            'new cm handle without trust level'      | 'ch-new'   | null                   || 2
+            scenario                 | registrationTrustLevel || expectedMapping
+            'with trusted cm handle' | TrustLevel.COMPLETE    || [ 'ch-1' : TrustLevel.COMPLETE ]
+            'without trust level'    | null                   || [ 'ch-1' : null ]
     }
 
     def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() {
@@ -311,17 +290,15 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     def 'Remove CmHandle Successfully: #scenario'() {
         given: 'a registration'
-            addPersistedYangModelCmHandles(['cmhandle'])
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
-                removedCmHandles: ['cmhandle'])
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle'])
         and: '#scenario'
-            mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >>
-                { if (!schemaSetExist) { throw new SchemaSetNotFoundException("", "") } }
+            mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >>  { if (!schemaSetExist) { throw new SchemaSetNotFoundException('', '') } }
         when: 'registration is updated to delete cmhandle'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'the cmHandle state is updated to "DELETING"'
-            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_)
-        and: 'method to delete relevant schema set is called once'
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >>
+                { args -> args[0].values()[0] == CmHandleState.DELETING }
+        then: 'method to delete relevant schema set is called once'
             1 * mockInventoryPersistence.deleteSchemaSetsWithCascade(_)
         and: 'method to delete relevant list/list element is called once'
             1 * mockInventoryPersistence.deleteDataNodes(_)
@@ -332,7 +309,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.cmHandle == 'cmhandle'
             }
         and: 'the cmHandle state is updated to "DELETED"'
-            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_)
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >>
+                { args -> args[0].values()[0] == CmHandleState.DELETED }
+        and: 'No cm handles state updates for "upgraded cm handles"'
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:])
         where:
             scenario                                            | schemaSetExist
             'schema-set exists and can be deleted successfully' | true
@@ -340,9 +320,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     }
 
     def 'Remove CmHandle: Partial Success'() {
-        given: 'some unique yang model cm handles'
-            addPersistedYangModelCmHandles(['cmhandle1', 'cmhandle2', 'cmhandle3'])
-        and: 'a registration with three cm-handles to be deleted'
+        given: 'a registration with three cm-handles to be deleted'
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3'])
         and: 'cm-handle deletion fails on batch'
@@ -359,7 +337,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle1')
         and: 'successfully de-registered cm handle 3 is removed from in progress map even though it was already being removed'
             1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle3') >> 'already in progress'
-        and: 'failed de-registered cm handle entries should not be removed from in progress map'
+        and: 'failed de-registered cm handle entries should NOT be removed from in progress map'
             0 * mockModuleSyncStartedOnCmHandles.remove('cmhandle2')
         and: '1st and 3rd cm-handle deletes successfully'
             with(response.removedCmHandles[0]) {
@@ -382,11 +360,13 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.size() == 2
                 assert it.every { entry -> entry.value == CmHandleState.DELETED }
             })
+        and: 'No cm handles state updates for "upgraded cm handles"'
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:])
+
     }
 
     def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() {
         given: 'a registration'
-            addPersistedYangModelCmHandles(['cmhandle'])
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle'])
         and: 'schema set batch deletion failed with unknown error'
@@ -413,7 +393,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     def 'Remove CmHandle Error Handling: #scenario'() {
         given: 'a registration'
-            addPersistedYangModelCmHandles(['cmhandle'])
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle'])
         and: 'cm-handle deletion fails on batch'
@@ -446,18 +425,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         when: 'the DMI plugin registration happens'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'the new alternate id is added to the cache'
-            1 * mockAlternateIdChecker.getIdsOfCmHandlesWithAcceptableAlternateId(ncmpServiceCmHandles) >> ['cmhandle1']
+            1 * mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(ncmpServiceCmHandles) >> ['cmhandle1']
     }
 
-    def getObjectUnderTest() {
-        return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
-                mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
-                stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
-                mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker))
-    }
-
-    def addPersistedYangModelCmHandles(ids) {
-        def yangModelCmHandles = ids.collect { new YangModelCmHandle(id:it) }
-        mockInventoryPersistence.getYangModelCmHandles(ids) >> yangModelCmHandles
-    }
 }
index fc548eb..9b4fe14 100644 (file)
@@ -23,6 +23,8 @@
 
 package org.onap.cps.ncmp.api.impl
 
+import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
+
 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
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -270,8 +272,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                     dmiDataPlugin: 'service2')
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
+        and: 'no rejected cm handles because of alternate ids'
+            mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
         when: 'parse and create cm handle in dmi registration then sync module'
-            objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration, ['test-cm-handle-id'])
+            mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
+            objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
         then: 'system persists the cm handle state'
             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
                 args -> {
index f41fd6c..aaa0344 100644 (file)
@@ -76,17 +76,17 @@ class AlternateIdCheckerSpec extends Specification {
             mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >>
                 {  args -> altAlreadyInDb.contains(args[0]) ? new DataNode() : throwDataNodeNotFoundException() }
         when: 'the batch of new cm handles is checked'
-            def result = objectUnderTest.getIdsOfCmHandlesWithAcceptableAlternateId(batch)
+            def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch)
         then: 'the result only contains the ids of the acceptable cm handles'
-            assert result.contains('ch-1') == acceptCh1
-            assert result.contains('ch-2') == acceptCh2
+            assert result.contains('ch-1') == rejectCh1
+            assert result.contains('ch-2') == rejectCh2
         where: 'the following alternate ids are used'
-            scenario                          | alt1   | alt2   | altAlreadyInDb  || acceptCh1 | acceptCh2
-            'no alternate ids'                | ''     | ''     | ['dont matter'] || true      | true
-            'new alternate ids'               | 'fdn1' | 'fdn2' | ['other fdn']   || true      | true
-            'one already used alternate id'   | 'fdn1' | 'fdn2' | ['fdn1']        || false     | true
-            'two already used alternate ids'  | 'fdn1' | 'fdn2' | ['fdn1','fdn2'] || false     | false
-            'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || true      | false
+            scenario                          | alt1   | alt2   | altAlreadyInDb  || rejectCh1 | rejectCh2
+            'no alternate ids'                | ''     | ''     | ['dont matter'] || false      | false
+            'new alternate ids'               | 'fdn1' | 'fdn2' | ['other fdn']   || false      | false
+            'one already used alternate id'   | 'fdn1' | 'fdn2' | ['fdn1']        || true       | false
+            'two already used alternate ids'  | 'fdn1' | 'fdn2' | ['fdn1','fdn2'] || true       | true
+            'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || false      | true
     }
 
     def throwDataNodeNotFoundException() {
index d76f912..7803ae3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
- *  Modifications Copyright (C) 2023 Nordix Foundation
+ *  Modifications Copyright (C) 2023-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.
@@ -21,6 +21,7 @@
 
 package org.onap.cps.ncmp.api.models
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
 
@@ -71,7 +72,7 @@ class CmHandleRegistrationResponseSpec extends Specification {
     def 'Failed cm-handle Registration with multiple responses.'() {
         when: 'cm-handle failure response is created for 2 xpaths'
             def cmHandleRegistrationResponses =
-                CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST)
+                CmHandleRegistrationResponse.createFailureResponsesFromXpaths(["somePathWithId[@id='123']", "somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST)
         then: 'the response has the correct cm handle ids'
             assert cmHandleRegistrationResponses.size() == 2
             assert cmHandleRegistrationResponses.stream().map(it -> it.cmHandle).collect(Collectors.toList())
@@ -81,9 +82,20 @@ class CmHandleRegistrationResponseSpec extends Specification {
     def 'Failed cm-handle Registration with multiple responses with an unexpected xpath.'() {
         when: 'cm-handle failure response is created for one valid and one unexpected xpath'
             def cmHandleRegistrationResponses =
-                CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST)
+                CmHandleRegistrationResponse.createFailureResponsesFromXpaths(["somePathWithId[@id='123']", "valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST)
         then: 'the response has only one entry'
             assert cmHandleRegistrationResponses.size() == 1
     }
 
+    def 'Failed cm-handle registration based on cm handle id and registration error'() {
+        when: 'the failure response is created with "alternate id already associated" error code for 1 cm handle'
+            def cmHandleRegistrationResponses =
+                    CmHandleRegistrationResponse.createFailureResponses(['ch 1'], ALTERNATE_ID_ALREADY_ASSOCIATED)
+        then: 'the response with expected values'
+            assert cmHandleRegistrationResponses[0].cmHandle == 'ch 1'
+            assert cmHandleRegistrationResponses[0].status == Status.FAILURE
+            assert cmHandleRegistrationResponses[0].ncmpResponseStatus == ALTERNATE_ID_ALREADY_ASSOCIATED
+            assert cmHandleRegistrationResponses[0].errorText == 'alternate id already associated'
+    }
+
 }
index 20a5ae3..90590a2 100644 (file)
@@ -1,6 +1,6 @@
 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
 .. http://creativecommons.org/licenses/by/4.0
-.. Copyright (C) 2023 Nordix Foundation
+.. Copyright (C) 2023-2024 Nordix Foundation
 
 .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
 .. _dataOperationMessageStatusCodes:
@@ -38,6 +38,8 @@ CPS-NCMP Message Status Codes
     +-----------------+------------------------------------------------------+-----------------------------------+
     | 110             | cm-handle has an invalid character(s) in id          | Inventory                         |
     +-----------------+------------------------------------------------------+-----------------------------------+
+    | 111             | alternate id already associated                      | Inventory                         |
+    +-----------------+------------------------------------------------------+-----------------------------------+
 
 .. note::