Change order of CM Handle Reference lookup depending on special character 98/140298/2
authorseanbeirne <sean.beirne@est.tech>
Thu, 20 Feb 2025 11:16:11 +0000 (11:16 +0000)
committerseanbeirne <sean.beirne@est.tech>
Thu, 27 Feb 2025 11:35:18 +0000 (11:35 +0000)
- implemented new algorithm using validator to rule out standard id / prefer alternate id
- moved validator imp to cps-service (not in RI) TBC!!!
- changed order of characters tested in validator to fail fast (on '=')
- added Boolean variation validator method to reduce overhead and prevent logic based on exceptions
- improved integration test to cover all scenarios
- add performance test for alternate id look up (report only)
- ensured all performance test use alternate ids it '='
- added small groovy tests to restore cps-ri code coverage to 0.31

Issue-ID: CPS-2605
Change-Id: Id9c22bb69904b7f5d376b7f8319332428435333e
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
Signed-off-by: seanbeirne <sean.beirne@est.tech>
12 files changed:
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy
cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java
cps-service/src/main/java/org/onap/cps/utils/CpsValidatorImpl.java [moved from cps-ri/src/main/java/org/onap/cps/ri/utils/CpsValidatorImpl.java with 81% similarity]
cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorImplSpec.groovy [moved from cps-ri/src/test/groovy/org/onap/cps/ri/utils/CpsValidatorImplSpec.groovy with 76% similarity]
integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/AlternateIdPerfTest.groovy [new file with mode: 0644]
integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy

index 189239c..7cb1c44 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation
+ *  Copyright (C) 2021-2025 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,7 +34,6 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.api.exceptions.CpsException;
-import org.onap.cps.api.exceptions.DataValidationException;
 import org.onap.cps.ncmp.api.NcmpResponseStatus;
 import org.onap.cps.ncmp.api.data.models.CmResourceAddress;
 import org.onap.cps.ncmp.api.data.models.DataOperationRequest;
@@ -79,7 +78,7 @@ public class DmiDataOperations {
      * This method fetches the resource data from the operational data store for a given CM handle
      * identifier on the specified resource using the DMI client.
      *
-     * @param cmResourceAddress Target datastore, CM handle, and resource identifier.
+     * @param cmResourceAddress Target datastore, CM handle reference, and resource identifier.
      * @param options           Options query string.
      * @param topic             Topic name for triggering asynchronous responses.
      * @param requestId         Request ID for asynchronous responses.
@@ -94,7 +93,8 @@ public class DmiDataOperations {
                                                                final String topic,
                                                                final String requestId,
                                                                final String authorization) {
-        final YangModelCmHandle yangModelCmHandle = resolveYangModelCmHandleFromCmHandleReference(cmResourceAddress);
+        final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(
+            cmResourceAddress.resolveCmHandleReferenceToId());
         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
         validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
         final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
@@ -157,22 +157,22 @@ public class DmiDataOperations {
      * This method creates the resource data from pass-through running data store for given cm handle
      * identifier on given resource using dmi client.
      *
-     * @param cmHandleId    network resource identifier
-     * @param resourceId    resource identifier
-     * @param operationType operation enum
-     * @param requestData   the request data
-     * @param dataType      data type
-     * @param authorization contents of Authorization header, or null if not present
+     * @param cmHandleReference network resource identifier
+     * @param resourceId        resource identifier
+     * @param operationType     operation enum
+     * @param requestData       the request data
+     * @param dataType          data type
+     * @param authorization     contents of Authorization header, or null if not present
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleId,
+    public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleReference,
                                                                              final String resourceId,
                                                                              final OperationType operationType,
                                                                              final String requestData,
                                                                              final String dataType,
                                                                              final String authorization) {
         final CmResourceAddress cmResourceAddress =
-                new CmResourceAddress(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId);
+                new CmResourceAddress(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleReference, resourceId);
 
         final YangModelCmHandle yangModelCmHandle =
             getYangModelCmHandle(cmResourceAddress.resolveCmHandleReferenceToId());
@@ -281,16 +281,6 @@ public class DmiDataOperations {
                 }).subscribe();
     }
 
-    private YangModelCmHandle resolveYangModelCmHandleFromCmHandleReference(final CmResourceAddress cmResourceAddress) {
-        String cmHandleId = cmResourceAddress.getCmHandleReference();
-        try {
-            return getYangModelCmHandle(cmHandleId);
-        } catch (final DataValidationException ignored) {
-            cmHandleId = cmResourceAddress.resolveCmHandleReferenceToId();
-            return getYangModelCmHandle(cmHandleId);
-        }
-    }
-
     private String createDmiDataOperationRequestAsJsonString(
             final List<DmiDataOperation> dmiDataOperationRequestBodies) {
         final DmiDataOperationRequest dmiDataOperationRequest = DmiDataOperationRequest.builder()
index b97da29..750a505 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2024 Nordix Foundation
+ *  Copyright (C) 2024-2025 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.onap.cps.ncmp.api.exceptions.CmHandleNotFoundException;
 import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException;
 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.onap.cps.utils.CpsValidator;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -33,6 +34,7 @@ import org.springframework.stereotype.Service;
 public class AlternateIdMatcher {
 
     private final InventoryPersistence inventoryPersistence;
+    private final CpsValidator cpsValidator;
 
     /**
      * Get yang model cm handle that matches longest alternate id by removing elements
@@ -64,11 +66,22 @@ public class AlternateIdMatcher {
      * @return cm handle id string
      */
     public String getCmHandleId(final String cmHandleReference) {
+        if (cpsValidator.isValidName(cmHandleReference)) {
+            return getCmHandleIdTryingStandardIdFirst(cmHandleReference);
+        }
+        return getCmHandleIdByAlternateId(cmHandleReference);
+    }
+
+    private String getCmHandleIdByAlternateId(final String cmHandleReference) {
+        // Please note: because of cm handle id validation rules this case does NOT need to try by (standard) id
+        return inventoryPersistence.getYangModelCmHandleByAlternateId(cmHandleReference).getId();
+    }
+
+    private String getCmHandleIdTryingStandardIdFirst(final String cmHandleReference) {
         if (inventoryPersistence.isExistingCmHandleId(cmHandleReference)) {
             return cmHandleReference;
-        } else {
-            return inventoryPersistence.getYangModelCmHandleByAlternateId(cmHandleReference).getId();
         }
+        return inventoryPersistence.getYangModelCmHandleByAlternateId(cmHandleReference).getId();
     }
 
     private String getParentPath(final String path, final String separator) {
index 01a08e7..3dd2eab 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation
+ *  Copyright (C) 2021-2025 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,8 +22,6 @@
 package org.onap.cps.ncmp.impl.data
 
 import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.api.exceptions.DataNodeNotFoundException
-import org.onap.cps.api.exceptions.DataValidationException
 import org.onap.cps.events.EventsPublisher
 import org.onap.cps.ncmp.api.data.models.CmResourceAddress
 import org.onap.cps.ncmp.api.data.models.DataOperationRequest
@@ -86,6 +84,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
 
     def 'call get resource data for #expectedDataStore from DMI without topic #scenario.'() {
         given: 'a cm handle for #cmHandleId'
+            alternateIdMatcher.getCmHandleId(cmHandleId) >> cmHandleId
             mockYangModelCmHandleRetrieval(dmiProperties)
         and: 'a positive response from DMI service when it is called with the expected parameters'
             def responseFromDmi = Mono.just(new ResponseEntity<Object>('{some-key:some-value}', HttpStatus.OK))
@@ -206,33 +205,6 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             CmHandleState.ADVISED || true
     }
 
-    def 'Resolving cm handle references with cm handle id.'() {
-        given: 'a resource address with a cm handle id'
-            def cmResourceAddress = new CmResourceAddress('some store', 'cm-handle-id', 'some resource')
-        and: 'the given cm handle id is available in the inventory'
-            mockInventoryPersistence.getYangModelCmHandle('cm-handle-id') >> yangModelCmHandle
-        expect: 'resolving the cm handle id returns the cm handle'
-            assert objectUnderTest.resolveYangModelCmHandleFromCmHandleReference(cmResourceAddress) == yangModelCmHandle
-    }
-
-    def 'Resolving cm handle references with alternate id #scenario.'() {
-        given: 'a resource with a alternate id'
-            def cmResourceAddress = new CmResourceAddress('some store', alternateId, 'some resource')
-        and: 'the alternate id cannot be found in the inventory directly and that results in an exception'
-            mockInventoryPersistence.getYangModelCmHandle(alternateId) >>  { throw errorThrownDuringCmHandleIdSearch }
-        and: 'the alternate id can be matched to a cm handle id'
-            alternateIdMatcher.getCmHandleId(alternateId) >> 'cm-handle-id'
-        and: 'that cm handle id is available in the inventory'
-            mockInventoryPersistence.getYangModelCmHandle('cm-handle-id') >> yangModelCmHandle
-        expect: 'resolving that cm handle id returns the cm handle'
-            assert objectUnderTest.resolveYangModelCmHandleFromCmHandleReference(cmResourceAddress) == yangModelCmHandle
-        where: 'the following alternate ids are used'
-            scenario                                  | alternateId     | errorThrownDuringCmHandleIdSearch
-            'alternate id with no special characters' | 'alternate-id'  | new DataNodeNotFoundException('','')
-            'alternate id with special characters'    | 'alternate#id'  | new DataValidationException('','')
-    }
-
-
     def extractDataValue(actualDataOperationCloudEvent) {
         return toTargetEvent(actualDataOperationCloudEvent, DataOperationEvent).data.responses[0]
     }
index a6d21af..fe7c5e3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2024 Nordix Foundation
+ *  Copyright (C) 2024-2025 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,12 +24,20 @@ import org.onap.cps.ncmp.api.exceptions.CmHandleNotFoundException
 import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException
 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.onap.cps.utils.CpsValidatorImpl
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
 import spock.lang.Specification
 
+@SpringBootTest
+@ContextConfiguration(classes = [InventoryPersistence, CpsValidatorImpl])
 class AlternateIdMatcherSpec extends Specification {
 
     def mockInventoryPersistence = Mock(InventoryPersistence)
-    def objectUnderTest = new AlternateIdMatcher(mockInventoryPersistence)
+
+    def mockCpsValidator = Mock(CpsValidatorImpl)
+
+    def objectUnderTest = new AlternateIdMatcher(mockInventoryPersistence, mockCpsValidator)
 
     def setup() {
         given: 'cm handle in the registry with alternate id /a/b'
@@ -72,13 +80,14 @@ class AlternateIdMatcherSpec extends Specification {
         when: 'a cmHandleCmReference is passed in'
             def result = objectUnderTest.getCmHandleId(cmHandleReference)
         then: 'the inventory persistence service returns a cm handle (or not)'
-            mockInventoryPersistence.isExistingCmHandleId(cmHandleReference) >> existingCmHandleIdResponse
+            mockCpsValidator.isValidName(cmHandleReference) >> existingCmHandleIdAndValidatorResponse
+            mockInventoryPersistence.isExistingCmHandleId(cmHandleReference) >> existingCmHandleIdAndValidatorResponse
             mockInventoryPersistence.getYangModelCmHandleByAlternateId(cmHandleReference) >> alternateIdGetResponse
         and: 'correct result is returned'
             assert result == cmHandleReference
         where: 'the following parameters are used'
-            cmHandleReference | existingCmHandleIdResponse | alternateIdGetResponse
-            'ch-1'            |  true                      |  ''
-            'alt-1'           |  false                     |  new YangModelCmHandle(id: 'alt-1')
+            cmHandleReference | existingCmHandleIdAndValidatorResponse | alternateIdGetResponse
+            'ch-1'            |  true                                  |  null
+            'alt=1'           |  false                                 |  new YangModelCmHandle(id: 'alt=1')
     }
 }
index 9abfdbe..4bf8c7c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  * Copyright (c) 2021 Bell Canada.
- * Modifications Copyright (C) 2022-2023 Nordix Foundation
+ * Modifications Copyright (C) 2022-2025 Nordix Foundation
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 package org.onap.cps.ri
 
 import org.hibernate.exception.ConstraintViolationException
+import org.onap.cps.ri.models.DataspaceEntity
 import org.onap.cps.ri.models.SchemaSetEntity
 import org.onap.cps.ri.repository.DataspaceRepository
 import org.onap.cps.ri.repository.ModuleReferenceRepository
@@ -30,7 +31,6 @@ import org.onap.cps.api.exceptions.DuplicatedYangResourceException
 import org.onap.cps.api.model.ModuleReference
 import org.springframework.dao.DataIntegrityViolationException
 import spock.lang.Specification
-
 import java.sql.SQLException
 
 /**
@@ -101,4 +101,32 @@ class CpsModulePersistenceServiceImplSpec extends Specification {
             noExceptionThrown()
     }
 
+    def 'Get yang schema resources.' () {
+        given: 'mocked methods for dataspace and schema set repositories'
+            mockDataspaceRepository.getByName('someDataspaceName') >> new DataspaceEntity()
+            mockSchemaSetRepository.getByDataspaceAndName(_,_) >> new SchemaSetEntity(yangResources: [])
+        when: 'the get yang schema resources method is called'
+            def result = objectUnderTest.getYangSchemaResources('someDataspaceName', 'someSchemaSetName')
+        then: 'an empty map is returned'
+            assert result.isEmpty()
+    }
+
+    def 'Get yang module references with just dataspace name.' () {
+        given: 'mocked method return yang resource repository'
+            mockYangResourceRepository.findAllModuleReferencesByDataspace('someDataspaceName') >> []
+        when: 'the get yang resource module reference method is called with 1 parameter'
+            def result = objectUnderTest.getYangResourceModuleReferences('someDataspaceName')
+        then: 'an empty collection is returned'
+            assert result.isEmpty()
+    }
+
+    def 'Get yang module references with dataspace name and anchor.' () {
+        given: 'mocked method return yang resource repository'
+            mockYangResourceRepository.findAllModuleReferencesByDataspaceAndAnchor('someDataspaceName', 'someAnchorName') >> []
+        when: 'the get yang resource module reference method is called with 2 parameters'
+            def result = objectUnderTest.getYangResourceModuleReferences('someDataspaceName','someAnchorName')
+        then: 'an empty collection is returned'
+            assert result.isEmpty()
+    }
+
 }
index 93f51ee..2244ea7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2025 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 
 package org.onap.cps.utils;
 
+import org.onap.cps.api.exceptions.DataValidationException;
 import org.onap.cps.api.parameters.PaginationOption;
 
 public interface CpsValidator {
 
+    /**
+     * Validate characters in name within cps.
+     *
+     * @param name name to be validated
+     * @return true if name is valid
+     */
+    boolean isValidName(final String name);
+
     /**
      * Validate characters in names within cps.
      *
-     * @param names names of data to be validated
+     * @param names names to be validated
+     * @throws DataValidationException for any name that is not valid.
      */
     void validateNameCharacters(final String... names);
 
     /**
      * Validate characters in names within cps.
      *
-     * @param names names of data to be validated
+     * @param names names  to be validated
+     * @throws DataValidationException for any name that is not valid.
      */
     void validateNameCharacters(final Iterable<String> names);
 
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2025 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.ri.utils;
+package org.onap.cps.utils;
 
 import com.google.common.collect.Lists;
 import java.util.Arrays;
 import java.util.Collection;
 import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.exceptions.DataValidationException;
 import org.onap.cps.api.parameters.PaginationOption;
-import org.onap.cps.utils.CpsValidator;
 import org.springframework.stereotype.Component;
 
-@Slf4j
 @Component
 @RequiredArgsConstructor
 public class CpsValidatorImpl implements CpsValidator {
 
-    private static final char[] UNSUPPORTED_NAME_CHARACTERS = "!\" #$%&'()*+,./\\:;<=>?@[]^`{|}~".toCharArray();
+    private static final char[] UNSUPPORTED_NAME_CHARACTERS = "=!\" #$%&'()*+,./\\:;<>?@[]^`{|}~".toCharArray();
+
+    @Override
+    public boolean isValidName(final String name) {
+        final Collection<Character> charactersOfName = Lists.charactersOf(name);
+        for (final char unsupportedCharacter : UNSUPPORTED_NAME_CHARACTERS) {
+            if (charactersOfName.contains(unsupportedCharacter)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
     @Override
     public void validateNameCharacters(final String... names) {
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2025 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.ri.utils
+package org.onap.cps.utils
 
-
-import org.onap.cps.api.parameters.PaginationOption
 import org.onap.cps.api.exceptions.DataValidationException
+import org.onap.cps.api.parameters.PaginationOption
 import spock.lang.Specification
 
 class CpsValidatorImplSpec extends Specification {
@@ -60,19 +59,38 @@ class CpsValidatorImplSpec extends Specification {
 
     def 'Validating a list of names with invalid names.'() {
         given: 'a list of names with an invalid name'
-            def names = ['valid-name', 'name with spaces']
+            def names = ['valid-name', 'invalid name with spaces']
         when: 'a list of strings is validated'
             objectUnderTest.validateNameCharacters(names)
         then: 'a data validation exception is thrown'
             thrown(DataValidationException)
     }
 
-    def 'Validate Pagination option with invalid page index and size.'() {
+    def 'Validate valid pagination options'() {
+        when: 'the pagination option is validated'
+            objectUnderTest.validatePaginationOption(option)
+        then: 'no exception occurs'
+            noExceptionThrown()
+        where: 'the following pagination options are used'
+            option << [null, new PaginationOption(1,2)]
+    }
+
+    def 'Validate invalid pagination.'() {
         when: 'the pagination option is validated using invalid options'
             objectUnderTest.validatePaginationOption(new PaginationOption(-5, -2))
         then: 'a data validation exception is thrown'
             def exceptionThrown = thrown(DataValidationException)
         and: 'the error was encountered at the following index in #scenario'
-            assert exceptionThrown.getDetails().contains("Invalid page index or size")
+            assert exceptionThrown.getDetails().contains('Invalid page index or size')
+    }
+
+
+    def 'Validation with boolean result.'() {
+        expect: 'validation returns expected boolean result'
+            assert objectUnderTest.isValidName(name) == expectedResult
+        where: 'following names are used'
+            name           || expectedResult
+            'valid-name'   || true
+            'invalid name' || false
     }
 }
index 693cf99..9b79af9 100644 (file)
@@ -281,7 +281,7 @@ abstract class CpsIntegrationSpecBase extends Specification {
         def modulePrefix = moduleNameStrategy.OVERLAPPING.equals(moduleNameStrategy) ? 'same' : moduleSetTag
         def moduleReferences = (1..200).collect {  "${modulePrefix}Module${it}" }
         (1..numberOfCmHandles).each {
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: "ch-${id}", moduleSetTag: moduleSetTag, alternateId: NO_ALTERNATE_ID)
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: "ch-${id}", moduleSetTag: moduleSetTag, alternateId: "alt=${id}")
             cmHandles.add(ncmpServiceCmHandle)
             dmiDispatcher1.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences
             dmiDispatcher2.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences
index 222b3c0..b1b777c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2024 Nordix Foundation
+ *  Copyright (C) 2024-2025 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the 'License');
  *  you may not use this file except in compliance with the License.
@@ -30,16 +30,13 @@ class AlternateIdSpec extends CpsIntegrationSpecBase {
 
     def setup() {
         dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
-        registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'alternateId')
     }
 
-    def cleanup() {
-        deregisterCmHandle(DMI1_URL, 'ch-1')
-    }
-
-    def 'AlternateId in pass-through data operations should return OK status.'() {
-        given: 'the URL for the pass-through data request'
-            def url = '/ncmp/v1/ch/alternateId/data/ds/ncmp-datastore:passthrough-running'
+    def 'Pass-through data operations using #scenario as reference.'() {
+        given: 'a cm handle with an alternate id'
+            registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, alternateId)
+        and: 'the URL for the pass-through data request'
+            def url = "/ncmp/v1/ch/${cmHandleReference}/data/ds/ncmp-datastore:passthrough-running"
         when: 'a pass-through data request is sent to NCMP'
             def response = mvc.perform(get(url)
                     .queryParam('resourceIdentifier', 'my-resource-id')
@@ -47,8 +44,13 @@ class AlternateIdSpec extends CpsIntegrationSpecBase {
                     .andReturn().response
         then: 'response status is Ok'
             assert response.status == HttpStatus.OK.value()
+        cleanup: 'remove the test cm handle'
+            deregisterCmHandle(DMI1_URL, 'ch-1')
+        where: 'the following ids are used'
+            scenario           | alternateId | cmHandleReference
+            'standard id'      | 'dont care' | 'ch-1'
+            'alt-id with ='    | 'alt=1'     | 'alt=1'
+            'alt-id without =' | 'alt-1'     | 'alt-1'
     }
 
-
-
 }
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/AlternateIdPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/AlternateIdPerfTest.groovy
new file mode 100644 (file)
index 0000000..b9d57cf
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2025 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.integration.performance.ncmp
+
+import org.onap.cps.integration.ResourceMeter
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+
+/**
+ * This test does not depend on common performance test data. Hence it just extends the integration spec base.
+ */
+class AlternateIdPerfTest extends CpsIntegrationSpecBase {
+
+    def resourceMeter = new ResourceMeter()
+
+    def 'Alternate Id Lookup Performance.'() {
+        given: 'register 1,000 cm handles (with alternative ids)'
+            registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, 'tagA', 1000, 1)
+        when: 'perform a 1,000 lookups by alternate id'
+            resourceMeter.start()
+            (1..1000).each {
+                networkCmProxyInventoryFacade.getNcmpServiceCmHandle("alt=${it}")
+            }
+            resourceMeter.stop()
+        then: 'record the result. Not asserted, just recorded in See https://lf-onap.atlassian.net/browse/CPS-2605'
+            println "*** CPS-2605 Execution time: ${resourceMeter.totalTimeInSeconds} ms"
+        cleanup: 'deregister test cm handles'
+            deregisterSequenceOfCmHandles(DMI1_URL, 1000, 1)
+    }
+}
index 5389732..dbf7e71 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2024 Nordix Foundation
+ *  Copyright (C) 2023-2025 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the 'License');
  *  you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ class CmHandleQueryPerfTest extends NcmpPerfTestBase {
                 cpsDataService.getDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR,
                         '/dmi-registry/cm-handles[@id="cm-' + it + '"]', OMIT_DESCENDANTS)
                 objectUnderTest.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR,
-                        '/dmi-registry/cm-handles[@alternate-id="alt-' + it + '"]', OMIT_DESCENDANTS)
+                        '/dmi-registry/cm-handles[@alternate-id="alt=' + it + '"]', OMIT_DESCENDANTS)
             }
             resourceMeter.stop()
         then: 'resource usage is as expected'
@@ -100,7 +100,7 @@ class CmHandleQueryPerfTest extends NcmpPerfTestBase {
             resourceMeter.start()
             (1..100).each {
                 count += cpsQueryService.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR,
-                        '/dmi-registry/cm-handles[@alternate-id="alt-' + it + '"]', OMIT_DESCENDANTS).size()
+                        '/dmi-registry/cm-handles[@alternate-id="alt=' + it + '"]', OMIT_DESCENDANTS).size()
             }
             resourceMeter.stop()
         then:
@@ -116,7 +116,7 @@ class CmHandleQueryPerfTest extends NcmpPerfTestBase {
 
     def 'A batch of CM-handles is looked up by alternate-id.'() {
         given: 'a CPS Path Query to look up 100 alternate-ids in a single operation'
-            def cpsPathQuery = '/dmi-registry/cm-handles[' + (1..100).collect { "@alternate-id='alt-${it}'" }.join(' or ') + ']'
+            def cpsPathQuery = '/dmi-registry/cm-handles[' + (1..100).collect { "@alternate-id='alt=${it}'" }.join(' or ') + ']'
         when: 'CM-handles are looked up by alternate-ids in a single query'
             resourceMeter.start()
             def count = cpsQueryService.queryDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, cpsPathQuery, OMIT_DESCENDANTS).size()