diff --git a/README.md b/README.md index 37ca0159..01afd57d 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,21 @@ Spring Application that exposes a subset of the APIs to manage configuration for needed by PagoPA SelfCare application. - [pagoPa Api Config - SelfCare integration](#pagopa-api-config---selfcare-integration) - * [Api Documentation ๐Ÿ“–](#api-documentation---) + * [Api Documentation ๐Ÿ“–](#api-documentation-) * [Technology Stack](#technology-stack) - * [Start Project Locally ๐Ÿš€](#start-project-locally---) + * [Start Project Locally ๐Ÿš€](#start-project-locally-) + [Prerequisites](#prerequisites) + [Run docker container](#run-docker-container) - * [Develop Locally ๐Ÿ’ป](#develop-locally---) + * [Develop Locally ๐Ÿ’ป](#develop-locally-) + [Prerequisites](#prerequisites-1) + [Run the project](#run-the-project) + [Spring Profiles](#spring-profiles) - + [Testing ๐Ÿงช](#testing---) + + [Testing ๐Ÿงช](#testing-) - [Unit testing](#unit-testing) - [Integration testing](#integration-testing) - [Performance testing](#performance-testing) - * [Contributors ๐Ÿ‘ฅ](#contributors---) - + [Mainteiners](#mainteiners) + * [Contributors ๐Ÿ‘ฅ](#contributors-) + + [Maintainers](#maintainers) --- @@ -69,11 +69,11 @@ from `./docker` directory Start the springboot application with this command for local test: -`mvn spring-boot:run -Dspring-boot.run.profiles=local` +`mvn spring-boot:run -Dspring.profiles.active=local` or, for H2 tests: -`mvn spring-boot:run -Dspring-boot.run.profiles=h2` +`mvn spring-boot:run -Dspring.profiles.active=h2` Using the spring profile `local`, the Spring application connects to the H2 in-memory DB. For access to H2 console, use this url: http://localhost:8080/h2-console/ @@ -111,6 +111,6 @@ install [k6](https://k6.io/) and then from `./performance-test/src` Made with โค๏ธ by PagoPa S.p.A. -### Mainteiners +### Maintainers See `CODEOWNERS` file diff --git a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/config/MappingsConfiguration.java b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/config/MappingsConfiguration.java index 5364d31b..55f2899b 100644 --- a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/config/MappingsConfiguration.java +++ b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/config/MappingsConfiguration.java @@ -3,17 +3,23 @@ import it.gov.pagopa.apiconfig.selfcareintegration.mapper.ConvertCanaliToChannelDetails; import it.gov.pagopa.apiconfig.selfcareintegration.mapper.ConvertIbanMasterToIbanDetail; import it.gov.pagopa.apiconfig.selfcareintegration.mapper.ConvertPaStazionePaToCreditorInstitutionDetail; +import it.gov.pagopa.apiconfig.selfcareintegration.mapper.ConvertPaToCreditorInstitutionInfo; import it.gov.pagopa.apiconfig.selfcareintegration.model.channel.ChannelDetails; import it.gov.pagopa.apiconfig.selfcareintegration.model.creditorinstitution.CreditorInstitutionDetail; +import it.gov.pagopa.apiconfig.selfcareintegration.model.creditorinstitution.CreditorInstitutionInfo; import it.gov.pagopa.apiconfig.selfcareintegration.model.iban.IbanDetails; import it.gov.pagopa.apiconfig.starter.entity.Canali; import it.gov.pagopa.apiconfig.starter.entity.IbanMaster; +import it.gov.pagopa.apiconfig.starter.entity.Pa; import it.gov.pagopa.apiconfig.starter.entity.PaStazionePa; import org.modelmapper.ModelMapper; import org.modelmapper.convention.MatchingStrategies; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * Configuration class for model mapper definitions + */ @Configuration public class MappingsConfiguration { @@ -23,16 +29,13 @@ ModelMapper modelMapper() { mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); // insert here the new mappers - mapper - .createTypeMap(Canali.class, ChannelDetails.class) - .setConverter(new ConvertCanaliToChannelDetails()); - mapper - .createTypeMap(IbanMaster.class, IbanDetails.class) - .setConverter(new ConvertIbanMasterToIbanDetail()); - mapper - .createTypeMap(PaStazionePa.class, CreditorInstitutionDetail.class) + mapper.createTypeMap(Canali.class, ChannelDetails.class).setConverter(new ConvertCanaliToChannelDetails()); + mapper.createTypeMap(IbanMaster.class, IbanDetails.class).setConverter(new ConvertIbanMasterToIbanDetail()); + mapper.createTypeMap(PaStazionePa.class, CreditorInstitutionDetail.class) .setConverter(new ConvertPaStazionePaToCreditorInstitutionDetail()); + mapper.createTypeMap(Pa.class, CreditorInstitutionInfo.class).setConverter(new ConvertPaToCreditorInstitutionInfo()); + return mapper; } } diff --git a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/controller/CreditorInstitutionController.java b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/controller/CreditorInstitutionController.java index 5cfc5ce7..a8eda249 100644 --- a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/controller/CreditorInstitutionController.java +++ b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/controller/CreditorInstitutionController.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -10,31 +11,39 @@ import io.swagger.v3.oas.annotations.tags.Tag; import it.gov.pagopa.apiconfig.selfcareintegration.model.ProblemJson; import it.gov.pagopa.apiconfig.selfcareintegration.model.code.CIAssociatedCodeList; +import it.gov.pagopa.apiconfig.selfcareintegration.model.creditorinstitution.CreditorInstitutionInfo; import it.gov.pagopa.apiconfig.selfcareintegration.model.station.StationDetailsList; import it.gov.pagopa.apiconfig.selfcareintegration.service.CreditorInstitutionsService; -import javax.validation.Valid; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.Positive; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.PageRequest; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import javax.validation.Valid; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Positive; +import java.util.List; + @RestController() @RequestMapping(path = "/creditorinstitutions") @Tag(name = "Creditor Institutions", description = "Everything about Creditor Institution") @Validated public class CreditorInstitutionController { - private final CreditorInstitutionsService creditorInstitutionsService; + private final CreditorInstitutionsService creditorInstitutionsService; + @Autowired public CreditorInstitutionController(CreditorInstitutionsService creditorInstitutionsService) { this.creditorInstitutionsService = creditorInstitutionsService; } @@ -259,4 +268,30 @@ public ResponseEntity getSegregationCodesFromCreditorInsti creditorInstitutionsService.getSegregationCodesFromCreditorInstitution( creditorInstitutionCode, showUsedCodes, service)); } + + /** + * Retrieve a list of creditor institution business names, given the provided list of creditor institution tax codes + * + * @param taxCodeList the list of creditor institution tax codes + * @return a list of {@link CreditorInstitutionInfo}, containing the business name and tax code of creditor institution + */ + @Operation(summary = "Get the list of creditor institution business names", + description = "Return a list of business name and tax code of creditor institutions, given the provided list of creditor institution tax codes", + security = {@SecurityRequirement(name = "ApiKey"), @SecurityRequirement(name = "Authorization")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK", + content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + array = @ArraySchema(schema = @Schema(implementation = CreditorInstitutionInfo.class)))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema())), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema())), + @ApiResponse(responseCode = "404", description = "Not Found", + 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)))}) + @PostMapping(value = "", produces = {MediaType.APPLICATION_JSON_VALUE}) + @Cacheable(value = "getCreditorInstitutionNamesFromTaxCodes") + public ResponseEntity> getCreditorInstitutionNamesFromTaxCodes(@RequestBody @NotEmpty List taxCodeList) { + return ResponseEntity.ok(this.creditorInstitutionsService.getCreditorInstitutionInfoList(taxCodeList)); + } } diff --git a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/exception/AppError.java b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/exception/AppError.java index fe87e195..2840e670 100644 --- a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/exception/AppError.java +++ b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/exception/AppError.java @@ -3,14 +3,21 @@ import lombok.Getter; import org.springframework.http.HttpStatus; +/** + * Enum defining the application errors + */ @Getter public enum AppError { INTERNAL_SERVER_ERROR( - HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", "Something was wrong"), + HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", "Something was wrong: %s"), CREDITOR_INSTITUTION_NOT_FOUND( HttpStatus.NOT_FOUND, "Creditor Institution not found", "No Creditor Institution found with code: %s"), + MULTIPLE_CREDITOR_INSTITUTIONS_NOT_FOUND( + HttpStatus.NOT_FOUND, + "Creditor Institutions not found", + "No Creditor Institutions found with codes: %s"), BROKER_NOT_FOUND(HttpStatus.NOT_FOUND, "Broker not found", "No Broker found with code: %s"), UNKNOWN(null, null, null); diff --git a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/mapper/ConvertPaToCreditorInstitutionInfo.java b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/mapper/ConvertPaToCreditorInstitutionInfo.java new file mode 100644 index 00000000..046afa2d --- /dev/null +++ b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/mapper/ConvertPaToCreditorInstitutionInfo.java @@ -0,0 +1,23 @@ +package it.gov.pagopa.apiconfig.selfcareintegration.mapper; + +import it.gov.pagopa.apiconfig.selfcareintegration.model.creditorinstitution.CreditorInstitutionInfo; +import it.gov.pagopa.apiconfig.starter.entity.Pa; +import org.modelmapper.Converter; +import org.modelmapper.spi.MappingContext; + +import javax.validation.Valid; + +/** + * Converter class, define how to map the {@link Pa} entity into the {@link CreditorInstitutionInfo} model + */ +public class ConvertPaToCreditorInstitutionInfo implements Converter { + + @Override + public CreditorInstitutionInfo convert(MappingContext context) { + @Valid Pa pa = context.getSource(); + return CreditorInstitutionInfo.builder() + .creditorInstitutionCode(pa.getIdDominio()) + .businessName(pa.getRagioneSociale() != null ? pa.getRagioneSociale() : "") + .build(); + } +} diff --git a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/model/creditorinstitution/CreditorInstitutionInfo.java b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/model/creditorinstitution/CreditorInstitutionInfo.java new file mode 100644 index 00000000..77cbb9b8 --- /dev/null +++ b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/model/creditorinstitution/CreditorInstitutionInfo.java @@ -0,0 +1,35 @@ +package it.gov.pagopa.apiconfig.selfcareintegration.model.creditorinstitution; + +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.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * Model that represent the name and tax code of a creditor institution + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreditorInstitutionInfo { + + @JsonProperty("business_name") + @Schema(example = "Comune di Roma", description = "The business name of the creditor institution", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull + private String businessName; + + @JsonProperty("creditor_institution_code") + @Schema(example = "02438750586", description = "The tax code of the creditor institution", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank + private String creditorInstitutionCode; +} diff --git a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/service/CreditorInstitutionsService.java b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/service/CreditorInstitutionsService.java index 10dadc97..b17fea73 100644 --- a/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/service/CreditorInstitutionsService.java +++ b/src/main/java/it/gov/pagopa/apiconfig/selfcareintegration/service/CreditorInstitutionsService.java @@ -4,6 +4,7 @@ import it.gov.pagopa.apiconfig.selfcareintegration.exception.AppException; import it.gov.pagopa.apiconfig.selfcareintegration.model.code.CIAssociatedCode; import it.gov.pagopa.apiconfig.selfcareintegration.model.code.CIAssociatedCodeList; +import it.gov.pagopa.apiconfig.selfcareintegration.model.creditorinstitution.CreditorInstitutionInfo; import it.gov.pagopa.apiconfig.selfcareintegration.model.station.StationDetails; import it.gov.pagopa.apiconfig.selfcareintegration.model.station.StationDetailsList; import it.gov.pagopa.apiconfig.selfcareintegration.repository.ExtendedCreditorInstitutionStationRepository; @@ -11,6 +12,15 @@ import it.gov.pagopa.apiconfig.starter.entity.Pa; import it.gov.pagopa.apiconfig.starter.entity.PaStazionePa; import it.gov.pagopa.apiconfig.starter.repository.PaRepository; +import org.modelmapper.ModelMapper; +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.Pageable; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import javax.validation.constraints.NotNull; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -18,142 +28,165 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.LongStream; -import javax.transaction.Transactional; -import javax.validation.constraints.NotNull; -import org.modelmapper.ModelMapper; -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.Pageable; -import org.springframework.stereotype.Service; +/** + * Service containing the method to access creditor institutions related data + */ @Service @Transactional public class CreditorInstitutionsService { - @Value("${sc-int.application_code.max_value}") - private Integer applicationCodeMaxValue; - - @Value("${sc-int.segregation_code.max_value}") - private Integer segregationCodeMaxValue; - - private final ExtendedCreditorInstitutionStationRepository ciStationRepository; - - private final PaRepository paRepository; - - private final ModelMapper modelMapper; - - public CreditorInstitutionsService(ExtendedCreditorInstitutionStationRepository ciStationRepository, PaRepository paRepository, ModelMapper modelMapper) { - this.ciStationRepository = ciStationRepository; - this.paRepository = paRepository; - this.modelMapper = modelMapper; - } - - public StationDetailsList getStationsDetailsFromCreditorInstitution( - @NotNull String creditorInstitutionCode, Pageable pageable) { - Pa pa = getPaIfExists(creditorInstitutionCode); - Page queryResult = ciStationRepository.findByFkPa(pa.getObjId(), pageable); - List stations = - queryResult.stream() - .map( - paStazionePa -> modelMapper.map(paStazionePa.getFkStazione(), StationDetails.class)) - .toList(); - return StationDetailsList.builder() - .pageInfo(Utility.buildPageInfo(queryResult)) - .stationsDetailsList(stations) - .build(); - } - - public CIAssociatedCodeList getApplicationCodesFromCreditorInstitution( - @NotNull String creditorInstitutionCode, boolean getUsed) { - Pa pa = getPaIfExists(creditorInstitutionCode); - List queryResult = ciStationRepository.findByFkPa(pa.getObjId()); - Map alreadyUsedApplicationCodes = - queryResult.stream() - .filter(station -> station.getProgressivo() != null) - .collect(Collectors.toMap(PaStazionePa::getProgressivo, station -> station)); - return extractUsedAndUnusedCodes(alreadyUsedApplicationCodes, applicationCodeMaxValue, getUsed); - } - - public CIAssociatedCodeList getSegregationCodesFromCreditorInstitution( - @NotNull String creditorInstitutionCode, boolean getUsed, String service) { - String serviceSubstringToBeSearched = service != null ? service.toLowerCase() : null; - Pa pa = getPaIfExists(creditorInstitutionCode); - List queryResult = ciStationRepository.findByFkPa(pa.getObjId()); - Map alreadyUsedApplicationCodes = - queryResult.stream() - .filter(station -> station.getSegregazione() != null) - .collect(Collectors.toMap(PaStazionePa::getSegregazione, station -> station)); - // get the set of codes to be obfuscated by service search. If passed service is null, the set - // is empty and all the element will be returned. - Set codesToBeObfuscated = - queryResult.stream() - .filter( - station -> { - String serviceEndpoint = station.getFkStazione().getServizio(); - return serviceSubstringToBeSearched != null - && (serviceEndpoint == null - || !serviceEndpoint.toLowerCase().contains(serviceSubstringToBeSearched)); - }) - .map(station -> station.getFkStazione().getIdStazione()) - .collect(Collectors.toSet()); - // retrieving the data removing the ones to be obfuscated - CIAssociatedCodeList ciAssociatedCodeList = - extractUsedAndUnusedCodes(alreadyUsedApplicationCodes, segregationCodeMaxValue, getUsed); - if (ciAssociatedCodeList.getUsedCodes() != null) { - ciAssociatedCodeList.setUsedCodes( - ciAssociatedCodeList.getUsedCodes().stream() - .filter(usedCode -> !codesToBeObfuscated.contains(usedCode.getStationName())) - .toList()); + private final Integer applicationCodeMaxValue; + + private final Integer segregationCodeMaxValue; + + private final ExtendedCreditorInstitutionStationRepository ciStationRepository; + + private final PaRepository paRepository; + + private final ModelMapper modelMapper; + + @Autowired + public CreditorInstitutionsService( + @Value("${sc-int.application_code.max_value}") Integer applicationCodeMaxValue, + @Value("${sc-int.segregation_code.max_value}") Integer segregationCodeMaxValue, + ExtendedCreditorInstitutionStationRepository ciStationRepository, + PaRepository paRepository, + ModelMapper modelMapper) { + this.applicationCodeMaxValue = applicationCodeMaxValue; + this.segregationCodeMaxValue = segregationCodeMaxValue; + this.ciStationRepository = ciStationRepository; + this.paRepository = paRepository; + this.modelMapper = modelMapper; + } + + public StationDetailsList getStationsDetailsFromCreditorInstitution( + @NotNull String creditorInstitutionCode, Pageable pageable) { + Pa pa = getPaIfExists(creditorInstitutionCode); + Page queryResult = ciStationRepository.findByFkPa(pa.getObjId(), pageable); + List stations = + queryResult.stream() + .map(paStazionePa -> modelMapper.map(paStazionePa.getFkStazione(), StationDetails.class)) + .toList(); + return StationDetailsList.builder() + .pageInfo(Utility.buildPageInfo(queryResult)) + .stationsDetailsList(stations) + .build(); + } + + public CIAssociatedCodeList getApplicationCodesFromCreditorInstitution( + @NotNull String creditorInstitutionCode, boolean getUsed) { + Pa pa = getPaIfExists(creditorInstitutionCode); + List queryResult = ciStationRepository.findByFkPa(pa.getObjId()); + Map alreadyUsedApplicationCodes = + queryResult.stream() + .filter(station -> station.getProgressivo() != null) + .collect(Collectors.toMap(PaStazionePa::getProgressivo, station -> station)); + return extractUsedAndUnusedCodes(alreadyUsedApplicationCodes, applicationCodeMaxValue, getUsed); + } + + public CIAssociatedCodeList getSegregationCodesFromCreditorInstitution( + @NotNull String creditorInstitutionCode, boolean getUsed, String service) { + String serviceSubstringToBeSearched = service != null ? service.toLowerCase() : null; + Pa pa = getPaIfExists(creditorInstitutionCode); + List queryResult = ciStationRepository.findByFkPa(pa.getObjId()); + Map alreadyUsedApplicationCodes = + queryResult.stream() + .filter(station -> station.getSegregazione() != null) + .collect(Collectors.toMap(PaStazionePa::getSegregazione, station -> station)); + // get the set of codes to be obfuscated by service search. If passed service is null, the set + // is empty and all the element will be returned. + Set codesToBeObfuscated = + queryResult.stream() + .filter( + station -> { + String serviceEndpoint = station.getFkStazione().getServizio(); + return serviceSubstringToBeSearched != null + && (serviceEndpoint == null + || !serviceEndpoint.toLowerCase().contains(serviceSubstringToBeSearched)); + }) + .map(station -> station.getFkStazione().getIdStazione()) + .collect(Collectors.toSet()); + // retrieving the data removing the ones to be obfuscated + CIAssociatedCodeList ciAssociatedCodeList = + extractUsedAndUnusedCodes(alreadyUsedApplicationCodes, segregationCodeMaxValue, getUsed); + if (ciAssociatedCodeList.getUsedCodes() != null) { + ciAssociatedCodeList.setUsedCodes( + ciAssociatedCodeList.getUsedCodes().stream() + .filter(usedCode -> !codesToBeObfuscated.contains(usedCode.getStationName())) + .toList()); + } + return ciAssociatedCodeList; } - return ciAssociatedCodeList; - } - - private CIAssociatedCodeList extractUsedAndUnusedCodes( - Map alreadyUsedCodes, long codeMaxValue, boolean includeUsed) { - List usedCodes = new LinkedList<>(); - List unusedCodes = new LinkedList<>(); - // extracting the used and unused code analyzing a sequence of N values and filtering by - // existing association to station - LongStream.rangeClosed(0, codeMaxValue) - .boxed() - .forEach( - codeFromSequence -> { - // generate model to be added - CIAssociatedCode ciAssociatedCode = - CIAssociatedCode.builder() - .code( - String.valueOf( - codeFromSequence < 10 - ? "0".concat(String.valueOf(codeFromSequence)) - : codeFromSequence)) - .build(); - // choose the list where must be added the model object - if (alreadyUsedCodes.containsKey(codeFromSequence)) { - ciAssociatedCode.setStationName( - alreadyUsedCodes.get(codeFromSequence).getFkStazione().getIdStazione()); - usedCodes.add(ciAssociatedCode); - } else { - unusedCodes.add(ciAssociatedCode); - } - }); - // generate final object - return CIAssociatedCodeList.builder() - .usedCodes(includeUsed ? usedCodes : null) - .unusedCodes(unusedCodes) - .build(); - } - - /** - * @param creditorInstitutionCode idDominio - * @return return the PA record from DB if Exists - * @throws AppException if not found - */ - protected Pa getPaIfExists(String creditorInstitutionCode) throws AppException { - Optional result = paRepository.findByIdDominio(creditorInstitutionCode); - if (result.isEmpty()) { - throw new AppException(AppError.CREDITOR_INSTITUTION_NOT_FOUND, creditorInstitutionCode); + + /** + * Retrieve the list of creditor institution business name given a list of tax code + * + * @param taxCodes the list of creditor institution tax codes + * @return the list of tax code and business name for the requested creditor institutions + */ + public List getCreditorInstitutionInfoList(List taxCodes) { + Optional> optionalPaList = this.paRepository.findByIdDominioIn(taxCodes); + + if (optionalPaList.isEmpty()) { + throw new AppException(AppError.MULTIPLE_CREDITOR_INSTITUTIONS_NOT_FOUND, taxCodes); + } + List paList = optionalPaList.get(); + + if (paList.size() != taxCodes.size()) { + throw new AppException(AppError.INTERNAL_SERVER_ERROR, "Retrieved less creditor institutions than expected"); + } + + return paList.stream() + .map(pa -> modelMapper.map(pa, CreditorInstitutionInfo.class)) + .collect(Collectors.toList()); + } + + private CIAssociatedCodeList extractUsedAndUnusedCodes( + Map alreadyUsedCodes, long codeMaxValue, boolean includeUsed) { + List usedCodes = new LinkedList<>(); + List unusedCodes = new LinkedList<>(); + // extracting the used and unused code analyzing a sequence of N values and filtering by + // existing association to station + LongStream.rangeClosed(0, codeMaxValue) + .boxed() + .forEach( + codeFromSequence -> { + // generate model to be added + CIAssociatedCode ciAssociatedCode = + CIAssociatedCode.builder() + .code( + String.valueOf( + codeFromSequence < 10 + ? "0".concat(String.valueOf(codeFromSequence)) + : codeFromSequence)) + .build(); + // choose the list where must be added the model object + if (alreadyUsedCodes.containsKey(codeFromSequence)) { + ciAssociatedCode.setStationName( + alreadyUsedCodes.get(codeFromSequence).getFkStazione().getIdStazione()); + usedCodes.add(ciAssociatedCode); + } else { + unusedCodes.add(ciAssociatedCode); + } + }); + // generate final object + return CIAssociatedCodeList.builder() + .usedCodes(includeUsed ? usedCodes : null) + .unusedCodes(unusedCodes) + .build(); + } + + /** + * @param creditorInstitutionCode idDominio + * @return return the PA record from DB if Exists + * @throws AppException if not found + */ + protected Pa getPaIfExists(String creditorInstitutionCode) throws AppException { + Optional result = paRepository.findByIdDominio(creditorInstitutionCode); + if (result.isEmpty()) { + throw new AppException(AppError.CREDITOR_INSTITUTION_NOT_FOUND, creditorInstitutionCode); + } + return result.get(); } - return result.get(); - } }