From c6aeaf1ed406d6e181f19144958704b933d06f57 Mon Sep 17 00:00:00 2001 From: alessio-acitelli <97529051+alessio-acitelli@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:04:33 +0200 Subject: [PATCH] feat: [PagoPa-1797] paging and caching (#70) * [PAGOPA-1797] paging and caching: first DRAFT impl * [PAGOPA-1797] paging and caching: REDIS impl --------- Co-authored-by: aacitelli --- helm/values-dev.yaml | 4 + helm/values-prod.yaml | 4 + helm/values-uat.yaml | 4 + pom.xml | 4 + .../pagopa/bizeventsservice/Application.java | 2 + .../bizeventsservice/config/RedisConfig.java | 54 +++++++++ .../controller/ITransactionController.java | 25 ++++ .../impl/TransactionController.java | 11 ++ .../entity/view/BizEventsViewUser.java | 11 +- ...nvertViewsToTransactionDetailResponse.java | 5 +- .../bizeventsservice/model/PageInfo.java | 44 +++++++ .../transaction/TransactionListResponse.java | 3 + .../TransactionListWrapResponse.java | 9 ++ .../BizEventsViewUserRepository.java | 3 + .../repository/redis/RedisRepository.java | 29 +++++ .../service/ITransactionService.java | 1 + .../service/impl/TransactionService.java | 75 +++++++++++- .../bizeventsservice/util/Constants.java | 1 + .../pagopa/bizeventsservice/util/Util.java | 51 +++++++++ src/main/resources/application-dev.properties | 7 ++ src/main/resources/application.properties | 7 ++ .../controller/TransactionControllerTest.java | 19 ++++ .../service/TransactionServiceTest.java | 107 +++++++++++++++++- .../bizeventsservice/util/ViewGenerator.java | 29 +++++ src/test/resources/application.properties | 7 ++ 25 files changed, 505 insertions(+), 11 deletions(-) create mode 100644 src/main/java/it/gov/pagopa/bizeventsservice/config/RedisConfig.java create mode 100644 src/main/java/it/gov/pagopa/bizeventsservice/model/PageInfo.java create mode 100644 src/main/java/it/gov/pagopa/bizeventsservice/repository/redis/RedisRepository.java create mode 100644 src/main/java/it/gov/pagopa/bizeventsservice/util/Util.java diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 12f1fb07..b7f920c1 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -86,6 +86,8 @@ microservice-chart: GENERATE_PDF_RETRY_MAX_DELAY: "10000" GENERATE_PDF_RETRY_MAX_ATTEMPTS: "1" CONNECTION_TIMEOUT: "10000" + REDIS_PORT: "6380" + REDIS_TTL: "5" # 5 minutes OTEL_SERVICE_NAME: "pagopabizeventsservice" OTEL_RESOURCE_ATTRIBUTES: "service.name=pagopareceiptspdfserviceotl,deployment.environment=dev" OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector.elastic-system.svc:4317" @@ -99,6 +101,8 @@ microservice-chart: COSMOS_DB_PRIMARY_KEY: 'cosmos-d-biz-key' PDF_RECEIPT_SUBSCRIPTION_KEY: "bizevent-d-receiptpdfservice-subscription-key" PDF_GENERATE_RECEIPT_SUBSCRIPTION_KEY: "bizevent-d-generatepdfservice-subscription-key" + REDIS_PWD: 'redis-password' + REDIS_HOST: 'redis-hostname' OTEL_EXPORTER_OTLP_HEADERS: 'elastic-otl-secret-token' keyvault: name: "pagopa-d-bizevents-kv" diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 3ec2157a..489b0dd6 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -86,6 +86,8 @@ microservice-chart: GENERATE_PDF_RETRY_MAX_DELAY: "10000" GENERATE_PDF_RETRY_MAX_ATTEMPTS: "1" CONNECTION_TIMEOUT: "10000" + REDIS_PORT: "6380" + REDIS_TTL: "20" # 20 minutes OTEL_SERVICE_NAME: "pagopabizeventsservice" OTEL_RESOURCE_ATTRIBUTES: "service.name=pagopareceiptspdfserviceotl,deployment.environment=prod" OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector.elastic-system.svc:4317" @@ -99,6 +101,8 @@ microservice-chart: COSMOS_DB_PRIMARY_KEY: 'cosmos-p-biz-key' PDF_RECEIPT_SUBSCRIPTION_KEY: "bizevent-p-receiptpdfservice-subscription-key" PDF_GENERATE_RECEIPT_SUBSCRIPTION_KEY: "bizevent-p-generatepdfservice-subscription-key" + REDIS_PWD: 'redis-password' + REDIS_HOST: 'redis-hostname' OTEL_EXPORTER_OTLP_HEADERS: 'elastic-otl-secret-token' keyvault: name: "pagopa-p-bizevents-kv" diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 5e630ad8..9247b702 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -86,6 +86,8 @@ microservice-chart: GENERATE_PDF_RETRY_MAX_DELAY: "10000" GENERATE_PDF_RETRY_MAX_ATTEMPTS: "1" CONNECTION_TIMEOUT: "10000" + REDIS_PORT: "6380" + REDIS_TTL: "20" # 20 minutes OTEL_SERVICE_NAME: "pagopabizeventsservice" OTEL_RESOURCE_ATTRIBUTES: "service.name=pagopareceiptspdfserviceotl,deployment.environment=uat" OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector.elastic-system.svc:4317" @@ -99,6 +101,8 @@ microservice-chart: COSMOS_DB_PRIMARY_KEY: 'cosmos-u-biz-key' PDF_RECEIPT_SUBSCRIPTION_KEY: "bizevent-u-receiptpdfservice-subscription-key" PDF_GENERATE_RECEIPT_SUBSCRIPTION_KEY: "bizevent-u-generatepdfservice-subscription-key" + REDIS_PWD: 'redis-password' + REDIS_HOST: 'redis-hostname' OTEL_EXPORTER_OTLP_HEADERS: 'elastic-otl-secret-token' keyvault: name: "pagopa-u-bizevents-kv" diff --git a/pom.xml b/pom.xml index 7fdc8e19..730c709b 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,10 @@ org.springframework.boot spring-boot-starter-validation + + org.springframework.boot + spring-boot-starter-data-redis + diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/Application.java b/src/main/java/it/gov/pagopa/bizeventsservice/Application.java index baa09e38..0f213810 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/Application.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/Application.java @@ -13,6 +13,8 @@ public class Application { public static void main(String[] args) { + // to avoid java.lang.ClassCastException for objects fetched from the REDIS cache + System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(Application.class, args); } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/config/RedisConfig.java b/src/main/java/it/gov/pagopa/bizeventsservice/config/RedisConfig.java new file mode 100644 index 00000000..a0e83fc6 --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/config/RedisConfig.java @@ -0,0 +1,54 @@ +package it.gov.pagopa.bizeventsservice.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + + @Value("${spring.redis.host}") + private String redisHost; + + @Value("${spring.redis.port}") + private int redisPort; + + @Value("${spring.redis.pwd}") + private String redisPwd; + + @Bean + public ObjectMapper objectMapper() { + final var objectMapper = new ObjectMapper().findAndRegisterModules(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + return objectMapper; + } + + @Bean + public LettuceConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisConfiguration = + new RedisStandaloneConfiguration(redisHost, redisPort); + redisConfiguration.setPassword(redisPwd); + LettuceClientConfiguration lettuceConfig = + LettuceClientConfiguration.builder().useSsl().build(); + return new LettuceConnectionFactory(redisConfiguration, lettuceConfig); + } + + @Bean + @Qualifier("object") + public RedisTemplate redisObjectTemplate( + final LettuceConnectionFactory connectionFactory, ObjectMapper objectMapper) { + RedisTemplate template = new RedisTemplate<>(); + template.setKeySerializer(new StringRedisSerializer()); + template.setConnectionFactory(connectionFactory); + return template; + } +} diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/controller/ITransactionController.java b/src/main/java/it/gov/pagopa/bizeventsservice/controller/ITransactionController.java index 010bd448..71b08b0f 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/controller/ITransactionController.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/controller/ITransactionController.java @@ -34,6 +34,7 @@ public interface ITransactionController { String X_CONTINUATION_TOKEN = "x-continuation-token"; String X_FISCAL_CODE = "x-fiscal-code"; String PAGE_SIZE = "size"; + String PAGE_NUMBER = "page"; /** * recovers biz-event data for the transaction list @@ -60,6 +61,30 @@ ResponseEntity getTransactionList( @RequestParam(name = PAGE_SIZE, required = false, defaultValue = "10") Integer size ); + + /** + * recovers biz-event data for the transaction list + * + * @param fiscalCode tokenized user fiscal code + * @param page optional parameter defining page number, default to 0 (first page) + * @param size optional parameter defining page size, defaults to 10 + * @return the transaction list + */ + @GetMapping(value = "/cached", produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Obtained transaction list.", + content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(name = "TransactionListWrapResponse", implementation = TransactionListWrapResponse.class))), + @ApiResponse(responseCode = "401", description = "Wrong or missing function key.", content = @Content(schema = @Schema())), + @ApiResponse(responseCode = "404", description = "Not found the transaction.", content = @Content(schema = @Schema(implementation = ProblemJson.class))), + @ApiResponse(responseCode = "429", description = "Too many requests.", content = @Content(schema = @Schema())), + @ApiResponse(responseCode = "500", description = "Service unavailable.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ProblemJson.class)))}) + @Operation(summary = "Retrieve the paged transaction list from biz events.", security = { + @SecurityRequirement(name = "ApiKey")}, operationId = "getTransactionList") + ResponseEntity getCachedTransactionList( + @RequestHeader(name = X_FISCAL_CODE) String fiscalCode, + @RequestParam(name = PAGE_NUMBER, required = false, defaultValue = "0") Integer page, + @RequestParam(name = PAGE_SIZE, required = false, defaultValue = "10") Integer size + ); @Operation(summary = "Retrieve the transaction details given its id.", security = { @SecurityRequirement(name = "ApiKey")}, operationId = "getTransactionDetails") diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/TransactionController.java b/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/TransactionController.java index cfee0cda..51fa3f54 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/TransactionController.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/TransactionController.java @@ -50,6 +50,17 @@ public ResponseEntity getTransactionList( .header(X_CONTINUATION_TOKEN, transactionListResponse.getContinuationToken()) .body(TransactionListWrapResponse.builder().transactions(transactionListResponse.getTransactionList()).build()); } + + @Override + public ResponseEntity getCachedTransactionList(String fiscalCode, + Integer page, Integer size) { + TransactionListResponse transactionListResponse = transactionService.getCachedTransactionList(fiscalCode, page, size); + return ResponseEntity.ok() + .body(TransactionListWrapResponse.builder() + .transactions(transactionListResponse.getTransactionList()) + .pageInfo(transactionListResponse.getPageInfo()) + .build()); + } @Override public ResponseEntity getTransactionDetails(String fiscalCode, String eventReference) { diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/entity/view/BizEventsViewUser.java b/src/main/java/it/gov/pagopa/bizeventsservice/entity/view/BizEventsViewUser.java index 2ae83543..6a79488d 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/entity/view/BizEventsViewUser.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/entity/view/BizEventsViewUser.java @@ -1,5 +1,7 @@ package it.gov.pagopa.bizeventsservice.entity.view; +import java.io.Serializable; + import com.azure.spring.data.cosmos.core.mapping.Container; import com.azure.spring.data.cosmos.core.mapping.GeneratedValue; import com.azure.spring.data.cosmos.core.mapping.PartitionKey; @@ -14,8 +16,13 @@ @Getter @Setter @Builder -public class BizEventsViewUser { - @GeneratedValue +public class BizEventsViewUser implements Serializable { + /** + * + */ + private static final long serialVersionUID = -4997399615775767480L; + + @GeneratedValue private String id; @PartitionKey private String taxCode; diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/mapper/ConvertViewsToTransactionDetailResponse.java b/src/main/java/it/gov/pagopa/bizeventsservice/mapper/ConvertViewsToTransactionDetailResponse.java index 87959523..5807fdec 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/mapper/ConvertViewsToTransactionDetailResponse.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/mapper/ConvertViewsToTransactionDetailResponse.java @@ -86,9 +86,6 @@ public static TransactionListItem convertTransactionListItem(BizEventsViewUser v totalAmount.updateAndGet(v -> v.add(amountExtracted)); } - // check if the cart contains an item in which the user is a debtor - boolean userHasDebtorItemInTheCart = listOfCartViews.stream().anyMatch(x -> viewUser.getTaxCode().equals(x.getDebtor().getTaxCode())); - return TransactionListItem.builder() .transactionId(viewUser.getTransactionId()) .payeeName(listOfCartViews.size() > 1 ? payeeCartName : listOfCartViews.get(0).getPayee().getName()) @@ -98,7 +95,7 @@ public static TransactionListItem convertTransactionListItem(BizEventsViewUser v .transactionDate(dateFormatZoned(viewUser.getTransactionDate())) .isCart(listOfCartViews.size() > 1) .isPayer(BooleanUtils.isTrue(viewUser.getIsPayer())) - .isDebtor(userHasDebtorItemInTheCart ? userHasDebtorItemInTheCart: BooleanUtils.isTrue(viewUser.getIsDebtor())) + .isDebtor(BooleanUtils.isTrue(viewUser.getIsDebtor())) .build(); } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/model/PageInfo.java b/src/main/java/it/gov/pagopa/bizeventsservice/model/PageInfo.java new file mode 100644 index 00000000..a663274b --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/model/PageInfo.java @@ -0,0 +1,44 @@ +package it.gov.pagopa.bizeventsservice.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class PageInfo { + + @JsonProperty("page") + @Schema(description = "Page number", required = true) + @PositiveOrZero + Integer page; + + @JsonProperty("limit") + @Schema(description = "Required number of items per page", required = true) + @Positive + Integer limit; + + @JsonProperty("items_found") + @Schema(description = "Number of items found. (The last page may have fewer elements than required)", required = true) + @PositiveOrZero + Integer itemsFound; + + @JsonProperty("total_pages") + @Schema(description = "Total number of pages", required = true) + @PositiveOrZero + Integer totalPages; +} diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListResponse.java b/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListResponse.java index 3760d8f6..65693eb6 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListResponse.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListResponse.java @@ -5,9 +5,12 @@ import java.util.List; +import it.gov.pagopa.bizeventsservice.model.PageInfo; + @Builder @Getter public class TransactionListResponse { private List transactionList; private String continuationToken; + private PageInfo pageInfo; } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListWrapResponse.java b/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListWrapResponse.java index 6e5f53c2..373c36ab 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListWrapResponse.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/model/response/transaction/TransactionListWrapResponse.java @@ -5,8 +5,17 @@ import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import it.gov.pagopa.bizeventsservice.model.PageInfo; + @Builder @Getter +@JsonInclude(Include.NON_NULL) public class TransactionListWrapResponse { private List transactions; + @JsonProperty("page_info") + private PageInfo pageInfo; } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsViewUserRepository.java b/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsViewUserRepository.java index a761ba27..06fc0cf5 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsViewUserRepository.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsViewUserRepository.java @@ -17,6 +17,9 @@ public interface BizEventsViewUserRepository extends CosmosRepository { @Query("select * from c where c.taxCode = @taxCode and c.hidden = false") Page getBizEventsViewUserByTaxCode(@Param("taxCode") String taxCode, Pageable pageable); + + @Query("select * from c where c.taxCode = @taxCode and c.hidden = false") + List getBizEventsViewUserByTaxCode(@Param("taxCode") String taxCode); @Query("select * from c where c.transactionId=@transactionId and c.taxCode = @fiscalCode and c.hidden = false") List getBizEventsViewUserByTaxCodeAndTransactionId(String fiscalCode, String transactionId); diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/repository/redis/RedisRepository.java b/src/main/java/it/gov/pagopa/bizeventsservice/repository/redis/RedisRepository.java new file mode 100644 index 00000000..2678b33f --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/repository/redis/RedisRepository.java @@ -0,0 +1,29 @@ +package it.gov.pagopa.bizeventsservice.repository.redis; + +import java.time.Duration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + + +@Component +public class RedisRepository { + + @Autowired + @Qualifier("object") + private RedisTemplate redisTemplateObj; + + public void save(String key, byte[] value, long ttl) { + redisTemplateObj.opsForValue().set(key, value, Duration.ofMinutes(ttl)); + } + + public byte[] get(String key) { + return redisTemplateObj.opsForValue().get(key); + } + + public void remove(String key) { + redisTemplateObj.delete(key); + } +} diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/service/ITransactionService.java b/src/main/java/it/gov/pagopa/bizeventsservice/service/ITransactionService.java index ebabfe94..b8caf0ea 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/service/ITransactionService.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/service/ITransactionService.java @@ -13,6 +13,7 @@ public interface ITransactionService { * @return transaction list */ TransactionListResponse getTransactionList(String fiscalCode, String continuationToken, Integer size); + TransactionListResponse getCachedTransactionList(String fiscalCode, Integer page, Integer size); TransactionDetailResponse getTransactionDetails(String fiscalCode, String transactionId); void disableTransaction(String fiscalCode, String transactionId); diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/TransactionService.java b/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/TransactionService.java index 5cf59467..97f0085b 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/TransactionService.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/TransactionService.java @@ -7,19 +7,29 @@ import it.gov.pagopa.bizeventsservice.exception.AppError; import it.gov.pagopa.bizeventsservice.exception.AppException; import it.gov.pagopa.bizeventsservice.mapper.ConvertViewsToTransactionDetailResponse; +import it.gov.pagopa.bizeventsservice.model.PageInfo; import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionDetailResponse; import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionListItem; import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionListResponse; import it.gov.pagopa.bizeventsservice.repository.BizEventsViewCartRepository; import it.gov.pagopa.bizeventsservice.repository.BizEventsViewGeneralRepository; import it.gov.pagopa.bizeventsservice.repository.BizEventsViewUserRepository; +import it.gov.pagopa.bizeventsservice.repository.redis.RedisRepository; import it.gov.pagopa.bizeventsservice.service.ITransactionService; +import it.gov.pagopa.bizeventsservice.util.Constants; +import it.gov.pagopa.bizeventsservice.util.Util; + +import org.apache.commons.lang3.SerializationUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import java.io.Serializable; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -30,12 +40,18 @@ public class TransactionService implements ITransactionService { private final BizEventsViewGeneralRepository bizEventsViewGeneralRepository; private final BizEventsViewCartRepository bizEventsViewCartRepository; private final BizEventsViewUserRepository bizEventsViewUserRepository; + private final RedisRepository redisRepository; + + @Value("${spring.redis.ttl}") + private long redisTTL; @Autowired - public TransactionService(BizEventsViewGeneralRepository bizEventsViewGeneralRepository, BizEventsViewCartRepository bizEventsViewCartRepository, BizEventsViewUserRepository bizEventsViewUserRepository) { + public TransactionService(BizEventsViewGeneralRepository bizEventsViewGeneralRepository, BizEventsViewCartRepository bizEventsViewCartRepository, + BizEventsViewUserRepository bizEventsViewUserRepository, RedisRepository redisRepository) { this.bizEventsViewGeneralRepository = bizEventsViewGeneralRepository; this.bizEventsViewCartRepository = bizEventsViewCartRepository; this.bizEventsViewUserRepository = bizEventsViewUserRepository; + this.redisRepository = redisRepository; } @Override @@ -47,7 +63,9 @@ public TransactionListResponse getTransactionList( final CosmosPageRequest pageRequest = new CosmosPageRequest(0, size, continuationToken, sort); final Page page = this.bizEventsViewUserRepository.getBizEventsViewUserByTaxCode(taxCode, pageRequest); Set set = new HashSet<>(page.getContent().size()); - List listOfViewUser = page.getContent().stream().filter(p -> set.add(p.getTransactionId())).toList(); + List listOfViewUser = page.getContent().stream() + .sorted(Comparator.comparing(BizEventsViewUser::getIsDebtor,Comparator.reverseOrder())) + .filter(p -> set.add(p.getTransactionId())).toList(); if(listOfViewUser.isEmpty()){ throw new AppException(AppError.VIEW_USER_NOT_FOUND_WITH_TAX_CODE, taxCode); @@ -74,6 +92,38 @@ public TransactionListResponse getTransactionList( .continuationToken(nextToken) .build(); } + + @Override + public TransactionListResponse getCachedTransactionList(String taxCode, Integer page, Integer size) { + + List listOfTransactionListItem = new ArrayList<>(); + + List> pagedListOfViewUser = this.retrievePaginatedList(taxCode, size); + + for (BizEventsViewUser viewUser : pagedListOfViewUser.get(page)) { + List listOfViewCart; + if(Boolean.TRUE.equals(viewUser.getIsPayer())){ + listOfViewCart = this.bizEventsViewCartRepository.getBizEventsViewCartByTransactionId(viewUser.getTransactionId()); + } else { + listOfViewCart = this.bizEventsViewCartRepository.getBizEventsViewCartByTransactionIdAndFilteredByTaxCode(viewUser.getTransactionId(), taxCode); + } + + if (!listOfViewCart.isEmpty()) { + TransactionListItem transactionListItem = ConvertViewsToTransactionDetailResponse.convertTransactionListItem(viewUser, listOfViewCart); + listOfTransactionListItem.add(transactionListItem); + } + } + + return TransactionListResponse.builder() + .transactionList(listOfTransactionListItem) + .pageInfo(PageInfo.builder() + .limit(size) + .page(page) + .itemsFound(pagedListOfViewUser.stream().mapToInt(i -> i.size()).sum()) + .totalPages(pagedListOfViewUser.size()) + .build()) + .build(); + } @Override public TransactionDetailResponse getTransactionDetails(String taxCode, String eventReference) { @@ -106,4 +156,25 @@ public void disableTransaction(String fiscalCode, String transactionId) { bizEventsViewUser.setHidden(true); bizEventsViewUserRepository.save(bizEventsViewUser); } + + private List> retrievePaginatedList (String taxCode, Integer size) { + List> pagedListOfViewUser = null; + byte [] data; + // read from the REDIS cache for the paginated list + if ((data=redisRepository.get(Constants.REDIS_KEY_PREFIX+taxCode)) != null){ + pagedListOfViewUser = SerializationUtils.deserialize(data); + } else { + List fullListOfViewUser = this.bizEventsViewUserRepository.getBizEventsViewUserByTaxCode(taxCode); + if(CollectionUtils.isEmpty(fullListOfViewUser)){ + throw new AppException(AppError.VIEW_USER_NOT_FOUND_WITH_TAX_CODE, taxCode); + } + pagedListOfViewUser = Util.getPaginatedList(fullListOfViewUser, size); + // write in the REDIS cache the paginated list + redisRepository.save(Constants.REDIS_KEY_PREFIX+taxCode, SerializationUtils.serialize((Serializable)pagedListOfViewUser), redisTTL); + } + return pagedListOfViewUser; + } + + + } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/util/Constants.java b/src/main/java/it/gov/pagopa/bizeventsservice/util/Constants.java index 516c155e..6811f415 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/util/Constants.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/util/Constants.java @@ -13,6 +13,7 @@ public class Constants { public static final String VIEW_GENERAL_NOT_FOUND = "Biz-events-view-general not found"; public static final String VIEW_CART_NOT_FOUND = "Biz-events-view-cart not found"; public static final String INVALID_DATA = "Invalid Data"; + public static final String REDIS_KEY_PREFIX = "trx_"; } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/util/Util.java b/src/main/java/it/gov/pagopa/bizeventsservice/util/Util.java new file mode 100644 index 00000000..e022feb6 --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/util/Util.java @@ -0,0 +1,51 @@ +package it.gov.pagopa.bizeventsservice.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import it.gov.pagopa.bizeventsservice.entity.view.BizEventsViewUser; + +public class Util { + + private Util() {} + + public static List> getPaginatedList(List fullListOfViewUser, int pageSize) { + Set set = new HashSet<>(fullListOfViewUser.size()); + // sorting based on the isDebtor field (true first) and then grouping by transactionId (the cart case requires that only one item be taken from those present) + List mergedListByTIDOfViewUser = fullListOfViewUser.stream() + .sorted(Comparator.comparing(BizEventsViewUser::getIsDebtor,Comparator.reverseOrder())) + .filter(p -> set.add(p.getTransactionId())).collect(Collectors.toList()); + + // sorting by transactionDate as per business request + Collections.sort(mergedListByTIDOfViewUser, + Comparator.comparing(BizEventsViewUser::getTransactionDate, + Comparator.nullsLast(Comparator.naturalOrder())) + .reversed()); + + return Util.getPages(mergedListByTIDOfViewUser, pageSize); + } + + public static List> getPages(Collection c, Integer pageSize) { + + List list = new ArrayList<>(c); + + if (pageSize == null || pageSize <= 0 || pageSize > list.size()) { + pageSize = list.size(); + } + + int numPages = (int) Math.ceil((double)list.size() / (double)pageSize); + List> pages = new ArrayList<>(numPages); + for (int pageNum = 0; pageNum < numPages; pageNum++) { + int toIndex = pageNum; + // subList() method is an instance of 'RandomAccessSubList' which is not serializable --> create a new ArrayList which is + pages.add(new ArrayList<>(list.subList(pageNum * pageSize, Math.min(++toIndex * pageSize, list.size())))); + } + return pages; + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 69cb62c9..9592b67d 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -24,6 +24,13 @@ service.get.pdf.receipt.host=https://api.dev.platform.pagopa.it/receipts/service service.generate.pdf.receipt.host=https://api.dev.platform.pagopa.it/receipts/helpdesk/v1 service.generate.pdf.receipt.path=/receipts/{event-id}/regenerate-receipt-pdf +# Redis configuration +spring.redis.host=pagopa-d-redis.redis.cache.windows.net +spring.redis.port=6380 +spring.redis.pwd=${REDIS_PWD} +# 5 min +spring.redis.ttl=5 + springdoc.group-configs[0].group=all springdoc.group-configs[0].displayName=Biz-Events All springdoc.group-configs[0].paths-to-match=/** diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index edcc11b0..0f6ec71e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -55,6 +55,13 @@ get.pdf.retry.maxDelay=${GET_PDF_RETRY_MAX_DELAY:10000} generate.pdf.retry.maxAttempts=${GENERATE_PDF_RETRY_MAX_ATTEMPTS:3} generate.pdf.retry.maxDelay=${GENERATE_PDF_RETRY_MAX_DELAY:10000} +# Redis +spring.redis.host=${REDIS_HOST} +spring.redis.port=${REDIS_PORT} +spring.redis.pwd=${REDIS_PWD} +# default 20 min +spring.redis.ttl=${REDIS_TTL:20} + # Openapi springdoc.use-fqn=false diff --git a/src/test/java/it/gov/pagopa/bizeventsservice/controller/TransactionControllerTest.java b/src/test/java/it/gov/pagopa/bizeventsservice/controller/TransactionControllerTest.java index 542798f6..669707f3 100644 --- a/src/test/java/it/gov/pagopa/bizeventsservice/controller/TransactionControllerTest.java +++ b/src/test/java/it/gov/pagopa/bizeventsservice/controller/TransactionControllerTest.java @@ -48,12 +48,15 @@ public class TransactionControllerTest { public static final String INVALID_FISCAL_CODE = "INVALID_TX_FISCAL_CODE"; public static final String VALID_FISCAL_CODE = "AAAAAA00A00A000A"; public static final String LIST_TRANSACTION_PATH = "/transactions"; + public static final String LIST_CACHED_TRANSACTION_PATH = "/transactions/cached"; public static final String FISCAL_CODE_HEADER_KEY = "x-fiscal-code"; public static final String TRANSACTION_DETAILS_PATH = "/transactions/transaction-id"; private static final String CONTINUATION_TOKEN_HEADER_KEY = "x-continuation-token"; public static final String CONTINUATION_TOKEN = "continuationToken"; public static final String SIZE_HEADER_KEY = "size"; public static final String SIZE = "10"; + public static final String PAGE_HEADER_KEY = "page"; + public static final String PAGE_NUM = "0"; public static final String TRANSACTION_DISABLE_PATH = "/transactions/transaction-id/disable"; public static final String TRANSACTION_RECEIPT_PATH = "/transactions/event-id/pdf"; @@ -81,6 +84,7 @@ void setUp() throws IOException { TransactionListResponse transactionListResponse = TransactionListResponse.builder().transactionList(transactionListItems).build(); TransactionDetailResponse transactionDetailResponse = TestUtil.readModelFromFile("biz-events/transactionDetails.json", TransactionDetailResponse.class); when(transactionService.getTransactionList(eq(VALID_FISCAL_CODE), anyString(), anyInt())).thenReturn(transactionListResponse); + when(transactionService.getCachedTransactionList(eq(VALID_FISCAL_CODE), anyInt(), anyInt())).thenReturn(transactionListResponse); when(transactionService.getTransactionDetails(anyString(), anyString())).thenReturn(transactionDetailResponse); Attachment attachmentDetail = mock (Attachment.class); AttachmentsDetailsResponse attachments = AttachmentsDetailsResponse.builder().attachments(Arrays.asList(attachmentDetail)).build(); @@ -123,6 +127,21 @@ void getListTransactionWithInvalidFiscalCodeShouldReturnError() throws Exception .andExpect(status().isBadRequest()) .andReturn(); } + + @Test + void getCachedListTransactionShouldReturnData() throws Exception { + MvcResult result = mvc.perform(get(LIST_CACHED_TRANSACTION_PATH) + .header(FISCAL_CODE_HEADER_KEY, VALID_FISCAL_CODE) + .queryParam(SIZE_HEADER_KEY, SIZE) + .queryParam(PAGE_HEADER_KEY, PAGE_NUM) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andReturn(); + assertNotNull(result.getResponse().getContentAsString()); + assertTrue(result.getResponse().getContentAsString().contains("b77d4987-a3e4-48d4-a2fd-af504f8b79e9")); + assertTrue(result.getResponse().getContentAsString().contains("100.0")); + } @Test void getTransactionDetailsShouldReturnData() throws Exception { diff --git a/src/test/java/it/gov/pagopa/bizeventsservice/service/TransactionServiceTest.java b/src/test/java/it/gov/pagopa/bizeventsservice/service/TransactionServiceTest.java index 455e4960..de60e407 100644 --- a/src/test/java/it/gov/pagopa/bizeventsservice/service/TransactionServiceTest.java +++ b/src/test/java/it/gov/pagopa/bizeventsservice/service/TransactionServiceTest.java @@ -9,8 +9,12 @@ import it.gov.pagopa.bizeventsservice.repository.BizEventsViewCartRepository; import it.gov.pagopa.bizeventsservice.repository.BizEventsViewGeneralRepository; import it.gov.pagopa.bizeventsservice.repository.BizEventsViewUserRepository; +import it.gov.pagopa.bizeventsservice.repository.redis.RedisRepository; import it.gov.pagopa.bizeventsservice.service.impl.TransactionService; +import it.gov.pagopa.bizeventsservice.util.Util; import it.gov.pagopa.bizeventsservice.util.ViewGenerator; + +import org.apache.commons.lang3.SerializationUtils; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; @@ -20,6 +24,7 @@ import org.springframework.http.HttpStatus; import org.springframework.test.context.ContextConfiguration; +import java.io.Serializable; import java.util.*; import static it.gov.pagopa.bizeventsservice.util.ViewGenerator.generateBizEventsViewUser; @@ -32,7 +37,9 @@ public class TransactionServiceTest { public static final String INVALID_FISCAL_CODE = "invalidFiscalCode"; public static final int PAGE_SIZE = 5; + public static final int PAGE_NUMBER = 0; public static final String CONTINUATION_TOKEN = "0"; + public static final String TRANSACTION_ID = "transactionId"; @MockBean private BizEventsViewUserRepository bizEventsViewUserRepository; @@ -40,6 +47,8 @@ public class TransactionServiceTest { private BizEventsViewGeneralRepository bizEventsViewGeneralRepository; @MockBean private BizEventsViewCartRepository bizEventsViewCartRepository; + @MockBean + private RedisRepository redisRepository; private TransactionService transactionService; @Value("${transaction.payee.cartName:Pagamento Multiplo}") @@ -47,7 +56,7 @@ public class TransactionServiceTest { @BeforeEach void setUp() { - transactionService = spy(new TransactionService(bizEventsViewGeneralRepository, bizEventsViewCartRepository, bizEventsViewUserRepository)); + transactionService = spy(new TransactionService(bizEventsViewGeneralRepository, bizEventsViewCartRepository, bizEventsViewUserRepository, redisRepository)); } @Test @@ -237,7 +246,7 @@ void transactionViewCartNotFoundThrowError() { Assertions.assertEquals(HttpStatus.NOT_FOUND, appException.getHttpStatus()); } @Test - public void transactionViewUserDisabled() { + void transactionViewUserDisabled() { List viewUserList = Collections.singletonList(generateBizEventsViewUser()); when(bizEventsViewUserRepository.getBizEventsViewUserByTaxCodeAndTransactionId(anyString(),anyString())) .thenReturn(viewUserList); @@ -248,7 +257,7 @@ public void transactionViewUserDisabled() { } @Test - public void transactionViewUserNotFoundThrowError() { + void transactionViewUserNotFoundThrowError() { when(bizEventsViewUserRepository.getBizEventsViewUserByTaxCodeAndTransactionId(anyString(),anyString())) .thenReturn(null); AppException appException = @@ -257,4 +266,96 @@ public void transactionViewUserNotFoundThrowError() { ViewGenerator.USER_TAX_CODE_WITH_TX, ViewGenerator.TRANSACTION_ID)); Assertions.assertEquals(HttpStatus.NOT_FOUND, appException.getHttpStatus()); } + + @Test + void notCachedTaxCodeWithEventsShouldReturnTransactionList() { + List listOfViewUser = ViewGenerator.generateListOf95MixedBizEventsViewUser(); + when(bizEventsViewUserRepository.getBizEventsViewUserByTaxCode(ViewGenerator.USER_TAX_CODE_WITH_TX)).thenReturn(listOfViewUser); + List listOfSingleViewCart = Collections.singletonList(ViewGenerator.generateBizEventsViewCart()); + List listOfMultiViewCart = ViewGenerator.generateListOfFiveViewCart(); + when(bizEventsViewCartRepository.getBizEventsViewCartByTransactionIdAndFilteredByTaxCode(contains("_cart_"), eq(ViewGenerator.USER_TAX_CODE_WITH_TX))) + .thenReturn(listOfMultiViewCart); + when(bizEventsViewCartRepository.getBizEventsViewCartByTransactionIdAndFilteredByTaxCode(contains("_nocart"), eq(ViewGenerator.USER_TAX_CODE_WITH_TX))) + .thenReturn(listOfSingleViewCart); + // taxcode is not in cache + when(redisRepository.get(anyString())).thenReturn(null); + + + // invoke service to get first page + TransactionListResponse transactionListResponse = + Assertions.assertDoesNotThrow(() -> + transactionService.getCachedTransactionList( + ViewGenerator.USER_TAX_CODE_WITH_TX, PAGE_NUMBER, PAGE_SIZE)); + Assertions.assertEquals(PAGE_NUMBER, transactionListResponse.getPageInfo().getPage()); + Assertions.assertEquals(18, transactionListResponse.getPageInfo().getTotalPages()); + Assertions.assertEquals(PAGE_SIZE, transactionListResponse.getPageInfo().getLimit()); + Assertions.assertEquals(88, transactionListResponse.getPageInfo().getItemsFound()); + List transactionListItems = transactionListResponse.getTransactionList(); + Assertions.assertNotNull(transactionListItems); + Assertions.assertEquals(5, transactionListItems.size()); + Assertions.assertEquals("2024-06-12T11:07:46Z", transactionListItems.get(0).getTransactionDate()); + + // invoke service to get last page + transactionListResponse = + Assertions.assertDoesNotThrow(() -> + transactionService.getCachedTransactionList( + ViewGenerator.USER_TAX_CODE_WITH_TX, 17, PAGE_SIZE)); + Assertions.assertEquals(17, transactionListResponse.getPageInfo().getPage()); + Assertions.assertEquals(18, transactionListResponse.getPageInfo().getTotalPages()); + Assertions.assertEquals(PAGE_SIZE, transactionListResponse.getPageInfo().getLimit()); + Assertions.assertEquals(88, transactionListResponse.getPageInfo().getItemsFound()); + transactionListItems = transactionListResponse.getTransactionList(); + Assertions.assertNotNull(transactionListItems); + Assertions.assertEquals(3, transactionListItems.size()); + Assertions.assertEquals("2024-06-07T11:07:46Z", transactionListItems.get(2).getTransactionDate()); + + verify(bizEventsViewUserRepository, times(2)).getBizEventsViewUserByTaxCode(ViewGenerator.USER_TAX_CODE_WITH_TX); + verifyNoMoreInteractions(bizEventsViewUserRepository); + } + + @Test + void cachedTaxCodeWithEventsShouldReturnTransactionList() { + List listOfViewUser = ViewGenerator.generateListOf95MixedBizEventsViewUser(); + when(bizEventsViewUserRepository.getBizEventsViewUserByTaxCode(ViewGenerator.USER_TAX_CODE_WITH_TX)).thenReturn(listOfViewUser); + List listOfSingleViewCart = Collections.singletonList(ViewGenerator.generateBizEventsViewCart()); + List listOfMultiViewCart = ViewGenerator.generateListOfFiveViewCart(); + when(bizEventsViewCartRepository.getBizEventsViewCartByTransactionIdAndFilteredByTaxCode(contains("_cart_"), eq(ViewGenerator.USER_TAX_CODE_WITH_TX))) + .thenReturn(listOfMultiViewCart); + when(bizEventsViewCartRepository.getBizEventsViewCartByTransactionIdAndFilteredByTaxCode(contains("_nocart"), eq(ViewGenerator.USER_TAX_CODE_WITH_TX))) + .thenReturn(listOfSingleViewCart); + // taxcode is in cache + byte[] data = SerializationUtils.serialize((Serializable)Util.getPaginatedList(listOfViewUser, PAGE_SIZE)); + when(redisRepository.get(anyString())).thenReturn(data); + + + // invoke service to get first page + TransactionListResponse transactionListResponse = + Assertions.assertDoesNotThrow(() -> + transactionService.getCachedTransactionList( + ViewGenerator.USER_TAX_CODE_WITH_TX, PAGE_NUMBER, PAGE_SIZE)); + Assertions.assertEquals(PAGE_NUMBER, transactionListResponse.getPageInfo().getPage()); + Assertions.assertEquals(18, transactionListResponse.getPageInfo().getTotalPages()); + Assertions.assertEquals(PAGE_SIZE, transactionListResponse.getPageInfo().getLimit()); + Assertions.assertEquals(88, transactionListResponse.getPageInfo().getItemsFound()); + List transactionListItems = transactionListResponse.getTransactionList(); + Assertions.assertNotNull(transactionListItems); + Assertions.assertEquals(5, transactionListItems.size()); + Assertions.assertEquals("2024-06-12T11:07:46Z", transactionListItems.get(0).getTransactionDate()); + + // invoke service to get last page + transactionListResponse = + Assertions.assertDoesNotThrow(() -> + transactionService.getCachedTransactionList( + ViewGenerator.USER_TAX_CODE_WITH_TX, 17, PAGE_SIZE)); + Assertions.assertEquals(17, transactionListResponse.getPageInfo().getPage()); + Assertions.assertEquals(18, transactionListResponse.getPageInfo().getTotalPages()); + Assertions.assertEquals(PAGE_SIZE, transactionListResponse.getPageInfo().getLimit()); + Assertions.assertEquals(88, transactionListResponse.getPageInfo().getItemsFound()); + transactionListItems = transactionListResponse.getTransactionList(); + Assertions.assertNotNull(transactionListItems); + Assertions.assertEquals(3, transactionListItems.size()); + Assertions.assertEquals("2024-06-07T11:07:46Z", transactionListItems.get(2).getTransactionDate()); + + verify(bizEventsViewUserRepository, times(0)).getBizEventsViewUserByTaxCode(ViewGenerator.USER_TAX_CODE_WITH_TX); + } } diff --git a/src/test/java/it/gov/pagopa/bizeventsservice/util/ViewGenerator.java b/src/test/java/it/gov/pagopa/bizeventsservice/util/ViewGenerator.java index d93bfc7b..97081365 100644 --- a/src/test/java/it/gov/pagopa/bizeventsservice/util/ViewGenerator.java +++ b/src/test/java/it/gov/pagopa/bizeventsservice/util/ViewGenerator.java @@ -51,6 +51,35 @@ public static List generateListOfFiveBizEventsViewUser(){ } return listOfViewUser; } + + public static List generateListOf95MixedBizEventsViewUser(){ + List listOfViewUser = new ArrayList<>(); + for(int i = 0; i < 85; i++){ + BizEventsViewUser viewUser = generateBizEventsViewUser(); + viewUser.setTransactionId(viewUser.getTransactionId()+"_nocart_"+i); + viewUser.setTransactionDate("2024-06-08T12:07:46Z"); + listOfViewUser.add(viewUser); + } + for(int i = 0; i < 5; i++){ + BizEventsViewUser viewUser = generateBizEventsViewUser(); + viewUser.setTransactionId(viewUser.getTransactionId()+"_cart_1"); + viewUser.setTransactionDate("2024-06-12T11:07:46Z"); + listOfViewUser.add(viewUser); + } + for(int i = 0; i < 3; i++){ + BizEventsViewUser viewUser = generateBizEventsViewUser(); + viewUser.setTransactionId(viewUser.getTransactionId()+"_cart_2"); + viewUser.setTransactionDate("2024-06-10T11:07:46Z"); + listOfViewUser.add(viewUser); + } + for(int i = 0; i < 2; i++){ + BizEventsViewUser viewUser = generateBizEventsViewUser(); + viewUser.setTransactionId(viewUser.getTransactionId()+"_cart_3"); + viewUser.setTransactionDate("2024-06-07T11:07:46Z"); + listOfViewUser.add(viewUser); + } + return listOfViewUser; + } public static BizEventsViewGeneral generateBizEventsViewGeneral(){ return BizEventsViewGeneral.builder() diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 68061748..e1570346 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -44,6 +44,13 @@ service.get.pdf.receipt.host=http://localhost:8080/receipts/service/v1 service.generate.pdf.receipt.host=http://localhost:8080/receipts/helpdesk/v1 service.generate.pdf.receipt.path=/receipts/{event-id}/regenerate-receipt-pdf +# Redis configuration +spring.redis.host=pagopa-d-redis.redis.cache.windows.net +spring.redis.port=6380 +spring.redis.pwd=pwd +# 5 min +spring.redis.ttl=5 + # Openapi springdoc.use-fqn=false springdoc.writer-with-order-by-keys=false