diff --git a/src/main/java/it/gov/pagopa/fdr/controller/interfaces/controller/IOrganizationsController.java b/src/main/java/it/gov/pagopa/fdr/controller/interfaces/controller/IOrganizationsController.java index 1d7f3325..b0db34d3 100644 --- a/src/main/java/it/gov/pagopa/fdr/controller/interfaces/controller/IOrganizationsController.java +++ b/src/main/java/it/gov/pagopa/fdr/controller/interfaces/controller/IOrganizationsController.java @@ -44,13 +44,14 @@ public interface IOrganizationsController { summary = "Get all published flow by creditor institution", description = """ -This API permits to search all published flows for a specific creditor institution, -formatted in a paginated view. The search can be enhanced including the PSP identifier -in order to filter only the flows for certain PSP.
-Before executing the query, the search filters are validated against entities configured for -Nodo dei Pagamenti environment, in particular on PSP (if that search filter is defined).
-The result of the query is paginated and contains all the metadata needed for pagination purposes.
-""") + This API permits to search all published flows for a specific creditor institution, + formatted in a paginated view. The search can be enhanced including the PSP identifier + in order to filter only the flows for certain PSP. The only flows retrieved are the latest + revision, as same as "nodoChiediElencoFlussiRendicontazione" primitive does.
+ Before executing the query, the search filters are validated against entities configured for + Nodo dei Pagamenti environment, in particular on PSP (if that search filter is defined).
+ The result of the query is paginated and contains all the metadata needed for pagination purposes.
+ """) @APIResponses( value = { @APIResponse( @@ -139,17 +140,17 @@ PaginatedFlowsResponse getAllPublishedFlows( summary = "Get single flow by creditor institution, searching by name and revision", description = """ -This API permits to search a single flows for a specific creditor institution. -In order to do so, it is required to add the following search filters: - - Creditor institution identifier: for filtering by specific organization - - PSP identifier: for filtering by flow-related PSP - - Flow name: for filtering by specific instance of the flow - - Revision: for filtering by flow revision + This API permits to search a single flows for a specific creditor institution. + In order to do so, it is required to add the following search filters: + - Creditor institution identifier: for filtering by specific organization + - PSP identifier: for filtering by flow-related PSP + - Flow name: for filtering by specific instance of the flow + - Revision: for filtering by flow revision -Before executing the query, the search filters are validated against entities configured for -Nodo dei Pagamenti environment, in particular on creditor institution and PSP. Also, -the name of the flow is validated against a specific standard format.
-""") + Before executing the query, the search filters are validated against entities configured for + Nodo dei Pagamenti environment, in particular on creditor institution and PSP. Also, + the name of the flow is validated against a specific standard format.
+ """) @APIResponses( value = { @APIResponse( @@ -230,18 +231,18 @@ SingleFlowResponse getSinglePublishedFlow( "Get all payments of single flow by creditor institution, searching by name and revision", description = """ -This API permits to search all the payments of single flows for a specific creditor institution, -formatted in a paginated view. In order to do so, it is required to add the following search filters: - - Creditor institution identifier: for filtering by specific organization - - PSP identifier: for filtering by flow-related PSP - - Flow name: for filtering by specific instance of the flow - - Revision: for filtering by flow revision + This API permits to search all the payments of single flows for a specific creditor institution, + formatted in a paginated view. In order to do so, it is required to add the following search filters: + - Creditor institution identifier: for filtering by specific organization + - PSP identifier: for filtering by flow-related PSP + - Flow name: for filtering by specific instance of the flow + - Revision: for filtering by flow revision -Before executing the query, the search filters are validated against entities configured for -Nodo dei Pagamenti environment, in particular on creditor institution and PSP. Also, -the name of the flow is validated against a specific standard format.
-The result of the query is paginated and contains all the metadata needed for pagination purposes.
-""") + Before executing the query, the search filters are validated against entities configured for + Nodo dei Pagamenti environment, in particular on creditor institution and PSP. Also, + the name of the flow is validated against a specific standard format.
+ The result of the query is paginated and contains all the metadata needed for pagination purposes.
+ """) @APIResponses( value = { @APIResponse( diff --git a/src/main/java/it/gov/pagopa/fdr/repository/FdrFlowRepository.java b/src/main/java/it/gov/pagopa/fdr/repository/FdrFlowRepository.java index a338edc9..a8500d4b 100644 --- a/src/main/java/it/gov/pagopa/fdr/repository/FdrFlowRepository.java +++ b/src/main/java/it/gov/pagopa/fdr/repository/FdrFlowRepository.java @@ -14,8 +14,10 @@ import it.gov.pagopa.fdr.util.common.StringUtil; import jakarta.enterprise.context.ApplicationScoped; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import org.eclipse.microprofile.faulttolerance.Retry; @ApplicationScoped public class FdrFlowRepository extends Repository { @@ -45,10 +47,11 @@ public class FdrFlowRepository extends Repository { + " and receiver.organization_id = :organizationId" + " and status != 'PUBLISHED'"; - public static final String QUERY_GET_PUBLISHED_BY_PSP_AND_NAME = - "sender.psp_id = :pspId and name = :flowName and status = 'PUBLISHED'"; + public static final String QUERY_GET_LAST_PUBLISHED_BY_PSP_AND_NAME = + "sender.psp_id = :pspId and name = :flowName and status = 'PUBLISHED' and is_latest =" + + " :isLatest"; - public RepositoryPagedResult findPublishedByOrganizationIdAndOptionalPspId( + public RepositoryPagedResult findLatestPublishedByOrganizationIdAndOptionalPspId( String organizationId, String pspId, Instant publishedGt, int pageNumber, int pageSize) { Parameters parameters = new Parameters(); @@ -73,6 +76,10 @@ public RepositoryPagedResult findPublishedByOrganizationIdAndOpti // setting mandatory field: flow status queryBuilder.add("status = :status"); parameters.and("status", FlowStatusEnum.PUBLISHED); + + // setting mandatory field: is_latest flag as true + queryBuilder.add("is_latest = :isLatest"); + parameters.and("isLatest", true); String queryString = String.join(" and ", queryBuilder); Page page = Page.of(pageNumber - 1, pageSize); @@ -137,14 +144,15 @@ public FdrFlowEntity findPublishedByOrganizationIdAndPspIdAndName( .orElse(null); } - public FdrFlowEntity findPublishedByPspIdAndName(String pspId, String flowName) { + public FdrFlowEntity findLastPublishedByPspIdAndName(String pspId, String flowName) { Parameters parameters = new Parameters(); parameters.and("pspId", pspId); parameters.and("flowName", flowName); + parameters.and("isLatest", true); return FdrFlowEntity.findByQuery( - FdrFlowRepository.QUERY_GET_PUBLISHED_BY_PSP_AND_NAME, parameters) + FdrFlowRepository.QUERY_GET_LAST_PUBLISHED_BY_PSP_AND_NAME, parameters) .project(FdrFlowEntity.class) .firstResultOptional() .orElse(null); @@ -230,11 +238,27 @@ public FdrFlowIdProjection findUnpublishedIdByPspIdAndNameAndOrganization( .orElse(null); } + public void updateLastPublishedAsNotLatest(String pspId, String flowName) { + + FdrFlowEntity entity = findLastPublishedByPspIdAndName(pspId, flowName); + if (entity != null) { + entity.setIsLatest(false); + updateEntity(entity); + } + } + public void createEntity(FdrFlowEntity entity) { entity.setTimestamp(Instant.now()); entity.persist(); } + // https://quarkus.io/guides/smallrye-fault-tolerance + @Retry( + delay = 1000, + maxRetries = -1, + maxDuration = 1, + durationUnit = ChronoUnit.MINUTES, + retryOn = Exception.class) public void updateEntity(FdrFlowEntity entity) { entity.setTimestamp(Instant.now()); entity.update(); diff --git a/src/main/java/it/gov/pagopa/fdr/repository/entity/flow/FdrFlowEntity.java b/src/main/java/it/gov/pagopa/fdr/repository/entity/flow/FdrFlowEntity.java index cbc0d583..c650c140 100644 --- a/src/main/java/it/gov/pagopa/fdr/repository/entity/flow/FdrFlowEntity.java +++ b/src/main/java/it/gov/pagopa/fdr/repository/entity/flow/FdrFlowEntity.java @@ -32,6 +32,9 @@ public class FdrFlowEntity extends PanacheMongoEntity { private Instant published; + @BsonProperty("is_latest") + private Boolean isLatest; + @BsonProperty("tot_amount") private Double totAmount; diff --git a/src/main/java/it/gov/pagopa/fdr/service/FlowService.java b/src/main/java/it/gov/pagopa/fdr/service/FlowService.java index 4ac8a4b1..cdd37466 100644 --- a/src/main/java/it/gov/pagopa/fdr/service/FlowService.java +++ b/src/main/java/it/gov/pagopa/fdr/service/FlowService.java @@ -23,6 +23,7 @@ import it.gov.pagopa.fdr.util.error.exception.common.AppException; import it.gov.pagopa.fdr.util.error.exception.persistence.PersistenceFailureException; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; import java.time.Instant; import java.util.concurrent.CompletableFuture; import org.bson.types.ObjectId; @@ -82,7 +83,7 @@ public PaginatedFlowsResponse getPaginatedPublishedFlowsForCI(FindFlowsByFilters SemanticValidator.validateGetPaginatedFlowsRequestForOrganizations(configData, args); RepositoryPagedResult paginatedResult = - this.flowRepository.findPublishedByOrganizationIdAndOptionalPspId( + this.flowRepository.findLatestPublishedByOrganizationIdAndOptionalPspId( organizationId, pspId, args.getPublishedGt(), (int) pageNumber, (int) pageSize); log.debugf( "Found [%s] entities in [%s] pages. Mapping data to final response.", @@ -245,7 +246,8 @@ public GenericResponse createEmptyFlow(String pspId, String flowName, CreateFlow } // retrieve the last published flow, in order to take its revision and increment it - FdrFlowEntity lastPublishedFlow = flowRepository.findPublishedByPspIdAndName(pspId, flowName); + FdrFlowEntity lastPublishedFlow = + flowRepository.findLastPublishedByPspIdAndName(pspId, flowName); Long revision = lastPublishedFlow != null ? (lastPublishedFlow.getRevision() + 1) : 1L; // finally, persist the newly generated entity @@ -294,13 +296,7 @@ public GenericResponse publishFlow(String pspId, String flowName, boolean isInte // check if retrieved flow can be published SemanticValidator.validatePublishingFlow(publishingFlow); - - // update the publishing flow in order to set its status to PUBLISHED - Instant now = Instant.now(); - publishingFlow.setUpdated(now); - publishingFlow.setPublished(now); - publishingFlow.setStatus(FlowStatusEnum.PUBLISHED); - this.flowRepository.updateEntity(publishingFlow); + publishNewRevision(pspId, flowName, publishingFlow); // TODO do this in transactional way // FdrFlowToHistoryEntity flowToHistoryEntity = flowMapper.toEntity(publishingFlow, @@ -358,4 +354,18 @@ private void deleteFlowPaymentsInAsync(ObjectId flowObjectId) { return null; }); } + + @Transactional(rollbackOn = Exception.class) + public void publishNewRevision(String pspId, String flowName, FdrFlowEntity publishingFlow) { + + // update the publishing flow in order to set its status to PUBLISHED + Instant now = Instant.now(); + publishingFlow.setUpdated(now); + publishingFlow.setPublished(now); + publishingFlow.setIsLatest(true); + publishingFlow.setStatus(FlowStatusEnum.PUBLISHED); + + this.flowRepository.updateLastPublishedAsNotLatest(pspId, flowName); + this.flowRepository.updateEntity(publishingFlow); + } }