From: FrancescoFioraEst Date: Fri, 16 May 2025 08:30:52 +0000 (+0100) Subject: Add pagination support in GET /compositions/{compositionId}/instances X-Git-Tag: 8.2.0~10 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=460501ec393398e6a8a12761aca3e2c0e1cb50f4;p=policy%2Fclamp.git Add pagination support in GET /compositions/{compositionId}/instances Issue-ID: POLICY-5359 Change-Id: Ic152f22c284cc69278429f601b37472b3f7a8be4 Signed-off-by: FrancescoFioraEst --- diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java index 3bfcf72ed..287ae5171 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java @@ -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 getAutomationCompositions(final UUID compositionId, final String name, - final String version) { - - return ProviderUtils - .asEntityList(automationCompositionRepository.findAll(createExample(compositionId, name, version))); + public List 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 createExample(final UUID compositionId, final String name, diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java index 67a05be16..c6ce2e08a 100644 --- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java +++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java @@ -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.>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.>any())) - .thenReturn(inputAutomationCompositionsJpa); acList = automationCompositionProvider.getAutomationCompositions(automationComposition.getCompositionId(), null, - null); + null, Pageable.unpaged()); + assertThat(acList).hasSize(2); + + when(automationCompositionRepository + .findAll(Mockito.>any(), Mockito.any(Pageable.class))) + .thenReturn(new PageImpl<>(inputAutomationCompositionsJpa)); + acList = automationCompositionProvider.getAutomationCompositions(automationComposition.getCompositionId(), null, + null, PageRequest.of(0, 10)); assertThat(acList).hasSize(2); } diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java index 1ae309d5d..c015ceb71 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java @@ -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; } diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java index c88ab69e0..2a45c5498 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java @@ -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 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)); } /** diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java index d5991e47c..bbcfddca8 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java @@ -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 queryCompositionInstances(UUID compositionId, String name, - String version, UUID xonaprequestid) { + String version, Integer page, Integer size, UUID xonaprequestid) { return stubUtils.getResponse(pathToAllInstances, AutomationCompositions.class); } diff --git a/runtime-acm/src/main/resources/openapi/openapi.yaml b/runtime-acm/src/main/resources/openapi/openapi.yaml index bf5c6a9dd..7a5f04d09 100644 --- a/runtime-acm/src/main/resources/openapi/openapi.yaml +++ b/runtime-acm/src/main/resources/openapi/openapi.yaml @@ -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 diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java index 759b6eec0..bfaa132f3 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java @@ -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 diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java index ca58fad51..30286db60 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java @@ -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()); }