Add pagination support in GET /compositions/{compositionId}/instances 95/140895/1
authorFrancescoFioraEst <francesco.fiora@est.tech>
Fri, 16 May 2025 08:30:52 +0000 (09:30 +0100)
committerFrancescoFioraEst <francesco.fiora@est.tech>
Fri, 16 May 2025 08:30:52 +0000 (09:30 +0100)
Issue-ID: POLICY-5359
Change-Id: Ic152f22c284cc69278429f601b37472b3f7a8be4
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java
models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java
runtime-acm/src/main/resources/openapi/openapi.yaml
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java

index 3bfcf72..287ae51 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2025 Nordix Foundation.
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
@@ -45,6 +45,7 @@ import org.onap.policy.common.parameters.ValidationStatus;
 import org.onap.policy.models.base.PfModelRuntimeException;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.springframework.data.domain.Example;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Isolation;
 import org.springframework.transaction.annotation.Transactional;
@@ -166,14 +167,14 @@ public class AutomationCompositionProvider {
      *
      * @param name the name of the automation composition to get, null to get all automation compositions
      * @param version the version of the automation composition to get, null to get all automation compositions
+     * @param pageable the Pageable
      * @return the automation compositions found
      */
     @Transactional(readOnly = true)
-    public List<AutomationComposition> getAutomationCompositions(final UUID compositionId, final String name,
-            final String version) {
-
-        return ProviderUtils
-                .asEntityList(automationCompositionRepository.findAll(createExample(compositionId, name, version)));
+    public List<AutomationComposition> getAutomationCompositions(@NonNull final UUID compositionId, final String name,
+            final String version, @NonNull final Pageable pageable) {
+        return ProviderUtils.asEntityList(automationCompositionRepository
+                .findAll(createExample(compositionId, name, version), pageable).toList());
     }
 
     private Example<JpaAutomationComposition> createExample(final UUID compositionId, final String name,
index 67a05be..c6ce2e0 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2025 Nordix Foundation.
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,6 +47,9 @@ import org.onap.policy.common.utils.coder.Coder;
 import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.common.utils.resources.ResourceUtils;
 import org.springframework.data.domain.Example;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
 
 class AutomationCompositionProviderTest {
 
@@ -103,6 +106,20 @@ class AutomationCompositionProviderTest {
         assertEquals(inputAutomationCompositions.getAutomationCompositionList().get(0), createdAutomationComposition);
     }
 
+    @Test
+    void testGetAutomationCompositionsWithNull() {
+        var automationCompositionProvider = new AutomationCompositionProvider(
+                mock(AutomationCompositionRepository.class), mock(AutomationCompositionElementRepository.class));
+
+        assertThatThrownBy(() -> automationCompositionProvider
+                .getAutomationCompositions(UUID.randomUUID(), null, null, null))
+                .hasMessage("pageable is marked non-null but is null");
+
+        assertThatThrownBy(() -> automationCompositionProvider
+                .getAutomationCompositions(null, null, null, Pageable.unpaged()))
+                .hasMessage("compositionId is marked non-null but is null");
+    }
+
     @Test
     void testGetAutomationCompositions() {
         var automationCompositionRepository = mock(AutomationCompositionRepository.class);
@@ -110,14 +127,22 @@ class AutomationCompositionProviderTest {
                 mock(AutomationCompositionElementRepository.class));
 
         var automationComposition = inputAutomationCompositions.getAutomationCompositionList().get(0);
+        when(automationCompositionRepository
+                .findAll(Mockito.<Example<JpaAutomationComposition>>any(), any(Pageable.class)))
+                .thenReturn(new PageImpl<>(inputAutomationCompositionsJpa));
         var acList = automationCompositionProvider.getAutomationCompositions(UUID.randomUUID(),
-                automationComposition.getName(), automationComposition.getVersion());
-        assertThat(acList).isEmpty();
+                automationComposition.getName(), automationComposition.getVersion(), Pageable.unpaged());
+        assertThat(acList).hasSize(2);
 
-        when(automationCompositionRepository.findAll(Mockito.<Example<JpaAutomationComposition>>any()))
-                .thenReturn(inputAutomationCompositionsJpa);
         acList = automationCompositionProvider.getAutomationCompositions(automationComposition.getCompositionId(), null,
-                null);
+                null, Pageable.unpaged());
+        assertThat(acList).hasSize(2);
+
+        when(automationCompositionRepository
+            .findAll(Mockito.<Example<JpaAutomationComposition>>any(), Mockito.any(Pageable.class)))
+            .thenReturn(new PageImpl<>(inputAutomationCompositionsJpa));
+        acList = automationCompositionProvider.getAutomationCompositions(automationComposition.getCompositionId(), null,
+            null, PageRequest.of(0, 10));
         assertThat(acList).hasSize(2);
     }
 
index 1ae309d..c015ceb 100644 (file)
@@ -59,6 +59,7 @@ import org.onap.policy.models.base.PfKey;
 import org.onap.policy.models.base.PfModelRuntimeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -450,13 +451,15 @@ public class AutomationCompositionInstantiationProvider {
      *
      * @param name the name of the automation composition to get, null for all automation compositions
      * @param version the version of the automation composition to get, null for all automation compositions
+     * @param pageable the Pageable
      * @return the automation compositions
      */
     @Transactional(readOnly = true)
-    public AutomationCompositions getAutomationCompositions(UUID compositionId, String name, String version) {
+    public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
+            final String name, final String version, @NonNull final Pageable pageable) {
         var automationCompositions = new AutomationCompositions();
         automationCompositions.setAutomationCompositionList(
-                automationCompositionProvider.getAutomationCompositions(compositionId, name, version));
+                automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
 
         return automationCompositions;
     }
index c88ab69..2a45c54 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation.
+ *  Copyright (C) 2021-2023,2025 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,6 +32,8 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
 import org.springframework.context.annotation.Profile;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -93,9 +95,9 @@ public class InstantiationController extends AbstractRestController implements A
      */
     @Override
     public ResponseEntity<AutomationCompositions> queryCompositionInstances(UUID compositionId, String name,
-            String version, UUID requestId) {
-
-        return ResponseEntity.ok().body(provider.getAutomationCompositions(compositionId, name, version));
+            String version, Integer page, Integer size, UUID requestId) {
+        var pageable = page != null && size != null ? PageRequest.of(page, size) : Pageable.unpaged();
+        return ResponseEntity.ok().body(provider.getAutomationCompositions(compositionId, name, version, pageable));
     }
 
     /**
index d5991e4..bbcfddc 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation.
+ *  Copyright (C) 2022-2023,2025 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -81,7 +81,7 @@ public class InstantiationControllerStub extends AbstractRestController implemen
 
     @Override
     public ResponseEntity<AutomationCompositions> queryCompositionInstances(UUID compositionId, String name,
-            String version, UUID xonaprequestid) {
+            String version, Integer page, Integer size, UUID xonaprequestid) {
         return stubUtils.getResponse(pathToAllInstances, AutomationCompositions.class);
     }
 
index bf5c6a9..7a5f04d 100644 (file)
@@ -998,6 +998,18 @@ paths:
           parameter is not specified, all automation composition instances for the specified definition that match the "name" filter are are returned.
         schema:
           type: string
+      - name: page
+        in: query
+        required: false
+        description: Zero-based page number, must not be negative.
+        schema:
+          type: integer
+      - name: size
+        in: query
+        required: false
+        description: The size of the page to be returned, must be greater than 0.
+        schema:
+          type: integer
       - name: X-onap-RequestId
         in: header
         description: RequestID for http transaction
index 759b6ee..bfaa132 100644 (file)
@@ -64,6 +64,7 @@ import org.onap.policy.models.base.PfConceptKey;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate;
+import org.springframework.data.domain.Pageable;
 
 /**
  * Class to perform unit test of {@link AutomationCompositionInstantiationProvider}}.
@@ -94,8 +95,6 @@ class AutomationCompositionInstantiationProviderTest {
 
     private static final String DO_NOT_MATCH = " do not match with ";
 
-
-
     private static ToscaServiceTemplate serviceTemplate = new ToscaServiceTemplate();
 
     @BeforeAll
@@ -106,6 +105,20 @@ class AutomationCompositionInstantiationProviderTest {
         serviceTemplate = jpa.toAuthorative();
     }
 
+    @Test
+    void testGetAutomationCompositionsWithNull() {
+        var instantiationProvider = new AutomationCompositionInstantiationProvider(
+                mock(AutomationCompositionProvider.class), mock(AcDefinitionProvider.class),
+                new AcInstanceStateResolver(), mock(SupervisionAcHandler.class), mock(ParticipantProvider.class),
+                CommonTestData.getTestParamaterGroup(), new EncryptionUtils(CommonTestData.getTestParamaterGroup()));
+        assertThatThrownBy(() -> instantiationProvider
+                .getAutomationCompositions(null, null, null, Pageable.unpaged()))
+                .hasMessage("compositionId is marked non-null but is null");
+        assertThatThrownBy(() -> instantiationProvider
+                .getAutomationCompositions(UUID.randomUUID(), null, null, null))
+                .hasMessage("pageable is marked non-null but is null");
+    }
+
     @Test
     void testInstantiationCrud() {
         var acDefinitionProvider = mock(AcDefinitionProvider.class);
@@ -133,10 +146,11 @@ class AutomationCompositionInstantiationProviderTest {
         verify(acProvider).createAutomationComposition(automationCompositionCreate);
 
         when(acProvider.getAutomationCompositions(compositionId, automationCompositionCreate.getName(),
-                automationCompositionCreate.getVersion())).thenReturn(List.of(automationCompositionCreate));
+                automationCompositionCreate.getVersion(), Pageable.unpaged()))
+                .thenReturn(List.of(automationCompositionCreate));
 
         var automationCompositionsGet = instantiationProvider.getAutomationCompositions(compositionId,
-                automationCompositionCreate.getName(), automationCompositionCreate.getVersion());
+                automationCompositionCreate.getName(), automationCompositionCreate.getVersion(), Pageable.unpaged());
         assertThat(automationCompositionCreate)
                 .isEqualTo(automationCompositionsGet.getAutomationCompositionList().get(0));
 
@@ -308,7 +322,6 @@ class AutomationCompositionInstantiationProviderTest {
 
         verify(supervisionAcHandler).migrate(any());
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationCompositionTarget);
-
     }
 
     @Test
index ca58fad..30286db 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation.
+ *  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -54,6 +54,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
 import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.data.domain.Pageable;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
@@ -66,6 +67,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
 @ActiveProfiles({ "test", "default" })
 class InstantiationControllerTest extends CommonRestController {
 
+    private static final int NUMBER_ISTANCES = 10;
     private static final String AC_INSTANTIATION_CREATE_JSON = "src/test/resources/rest/acm/AutomationComposition.json";
     private static final String AC_VERSIONING_YAML = "src/test/resources/rest/acm/AutomationCompositionVersioning.yaml";
 
@@ -202,10 +204,11 @@ class InstantiationControllerTest extends CommonRestController {
     void testQuery_NoResultWithThisName() {
         var invocationBuilder =
                 super.sendRequest(getInstanceEndPoint(UUID.randomUUID()) + "?name=noResultWithThisName");
-        var rawresp = invocationBuilder.buildGet().invoke();
-        assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
-        var resp = rawresp.readEntity(AutomationCompositions.class);
-        assertThat(resp.getAutomationCompositionList()).isEmpty();
+        try (var rawresp = invocationBuilder.buildGet().invoke()) {
+            assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+            var resp = rawresp.readEntity(AutomationCompositions.class);
+            assertThat(resp.getAutomationCompositionList()).isEmpty();
+        }
     }
 
     @Test
@@ -219,14 +222,55 @@ class InstantiationControllerTest extends CommonRestController {
 
         var invocationBuilder = super.sendRequest(
                 getInstanceEndPoint(compositionId) + "?name=" + automationComposition.getKey().getName());
-        var rawresp = invocationBuilder.buildGet().invoke();
-        assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
-        var automationCompositionsQuery = rawresp.readEntity(AutomationCompositions.class);
-        assertNotNull(automationCompositionsQuery);
-        assertThat(automationCompositionsQuery.getAutomationCompositionList()).hasSize(1);
-        var automationCompositionRc = automationCompositionsQuery.getAutomationCompositionList().get(0);
-        automationComposition.setLastMsg(automationCompositionRc.getLastMsg());
-        assertEquals(automationComposition, automationCompositionRc);
+        try (var rawresp = invocationBuilder.buildGet().invoke()) {
+            assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+            var automationCompositionsQuery = rawresp.readEntity(AutomationCompositions.class);
+            assertNotNull(automationCompositionsQuery);
+            assertThat(automationCompositionsQuery.getAutomationCompositionList()).hasSize(1);
+            var automationCompositionRc = automationCompositionsQuery.getAutomationCompositionList().get(0);
+            automationComposition.setLastMsg(automationCompositionRc.getLastMsg());
+            assertEquals(automationComposition, automationCompositionRc);
+        }
+    }
+
+    @Test
+    void testQueryPageable() {
+        var compositionId = createAcDefinitionInDB("Query");
+        var automationComposition =
+            InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Query");
+        automationComposition.setCompositionId(compositionId);
+        for (var i = 0; i < NUMBER_ISTANCES; i++) {
+            automationComposition.setName("acm_" + i);
+            instantiationProvider.createAutomationComposition(compositionId, automationComposition);
+        }
+        var endpoint = getInstanceEndPoint(compositionId);
+        validateQueryPageable(endpoint + "?name=wrong_name", 0);
+        validateQueryPageable(endpoint + "?name=acm_1", 1);
+        validateQueryPageable(endpoint + "?page=1&size=4", 4);
+
+        validateQueryNotPageable(endpoint + "?page=0");
+        validateQueryNotPageable(endpoint + "?size=5");
+        validateQueryNotPageable(endpoint);
+    }
+
+    private void validateQueryNotPageable(String link) {
+        var invocationBuilder = super.sendRequest(link);
+        try (var response = invocationBuilder.buildGet().invoke()) {
+            assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+            var resultList = response.readEntity(AutomationCompositions.class);
+            assertNotNull(resultList);
+            assertThat(resultList.getAutomationCompositionList()).hasSizeGreaterThanOrEqualTo(NUMBER_ISTANCES);
+        }
+    }
+
+    private void validateQueryPageable(String link, int size) {
+        var invocationBuilder = super.sendRequest(link);
+        try (var response = invocationBuilder.buildGet().invoke()) {
+            assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+            var resultList = response.readEntity(AutomationCompositions.class);
+            assertNotNull(resultList);
+            assertThat(resultList.getAutomationCompositionList()).hasSize(size);
+        }
     }
 
     @Test
@@ -271,8 +315,9 @@ class InstantiationControllerTest extends CommonRestController {
         var instResponse = resp.readEntity(InstantiationResponse.class);
         InstantiationUtils.assertInstantiationResponse(instResponse, automationComposition);
 
-        var automationCompositionsFromDb = instantiationProvider.getAutomationCompositions(compositionId,
-                automationComposition.getKey().getName(), automationComposition.getKey().getVersion());
+        var automationCompositionsFromDb = instantiationProvider.getAutomationCompositions(
+                compositionId, automationComposition.getKey().getName(),
+                automationComposition.getKey().getVersion(), Pageable.unpaged());
 
         assertNotNull(automationCompositionsFromDb);
         assertThat(automationCompositionsFromDb.getAutomationCompositionList()).hasSize(1);
@@ -298,7 +343,8 @@ class InstantiationControllerTest extends CommonRestController {
         InstantiationUtils.assertInstantiationResponse(instResponse, automationCompositionFromRsc);
 
         var automationCompositionsFromDb = instantiationProvider.getAutomationCompositions(compositionId,
-                automationCompositionFromRsc.getKey().getName(), automationCompositionFromRsc.getKey().getVersion());
+                automationCompositionFromRsc.getKey().getName(), automationCompositionFromRsc.getKey().getVersion(),
+                Pageable.unpaged());
         assertEquals(DeployState.DELETING,
                 automationCompositionsFromDb.getAutomationCompositionList().get(0).getDeployState());
     }