diff --git a/.github/workflows/create_release_branch.yml b/.github/workflows/create_release_branch.yml new file mode 100644 index 000000000..40f2b7e5c --- /dev/null +++ b/.github/workflows/create_release_branch.yml @@ -0,0 +1,44 @@ +name: Create Release branch + +on: + workflow_dispatch: + inputs: + version-bump: + required: false + type: choice + options: + - major + - minor + - patch + default: patch + description: 'Major, Minor, or Patch version bump' + +jobs: + create_branch: + name: 'Create Release Branch' + runs-on: ubuntu-20.04 + permissions: + contents: write + actions: write + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + + - name: Create Release Branch + id: create_branch + uses: pagopa/selfcare-commons/github-actions-template/create-release@main + with: + version_bump: ${{ inputs.version-bump }} + github_path_token: ${{ secrets.GH_PAT_VARIABLES }} + + - name: Trigger release ms UAT Release + run: | + gh workflow run release_ms.yml \ + --ref ${{ steps.create_branch.outputs.new_branch_name }} + + - name: Trigger PNPG release ms UAT Release + run: | + gh workflow run release_ms_pnpg.yml \ + --ref ${{ steps.create_branch.outputs.new_branch_name }} + diff --git a/.github/workflows/release_ms.yml b/.github/workflows/release_ms.yml index 4ea26836f..ce82763e5 100644 --- a/.github/workflows/release_ms.yml +++ b/.github/workflows/release_ms.yml @@ -56,4 +56,14 @@ jobs: with: environment: prod tf_environment: prod - dir: 'infra/container_apps' \ No newline at end of file + dir: 'infra/container_apps' + + promote_release: + name: 'Promote prerelase release' + runs-on: ubuntu-20.04 + needs: [release_prod] + steps: + - uses: pagopa/selfcare-commons/github-actions-template/promote-release@main + with: + github_path_token: ${{ secrets.GH_PAT_VARIABLES }} + release_version: ${{ vars.CURRENT_UAT_VERSION }} \ No newline at end of file diff --git a/app/src/main/resources/swagger/api-docs.json b/app/src/main/resources/swagger/api-docs.json index 13d87226e..f9edc4ff5 100644 --- a/app/src/main/resources/swagger/api-docs.json +++ b/app/src/main/resources/swagger/api-docs.json @@ -3886,6 +3886,49 @@ } ] } }, + "/notification-event/users/count" : { + "get" : { + "tags" : [ "kafka" ], + "summary" : "countUsers", + "description" : "Users' Count for single product", + "operationId" : "countUsersUsingGET", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/ProductCountResponse" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + }, + "security" : [ { + "bearerAuth" : [ "global" ] + } ] + } + }, "/tokens" : { "get" : { "tags" : [ "Token" ], @@ -7164,6 +7207,31 @@ } } }, + "ProductCount" : { + "title" : "ProductCount", + "type" : "object", + "properties" : { + "count" : { + "type" : "integer", + "format" : "int32" + }, + "productId" : { + "type" : "string" + } + } + }, + "ProductCountResponse" : { + "title" : "ProductCountResponse", + "type" : "object", + "properties" : { + "products" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ProductCount" + } + } + } + }, "ProductInfo" : { "title" : "ProductInfo", "type" : "object", diff --git a/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserConnector.java b/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserConnector.java index bb446b4da..63ffb9801 100644 --- a/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserConnector.java +++ b/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserConnector.java @@ -2,6 +2,7 @@ import it.pagopa.selfcare.commons.base.security.PartyRole; import it.pagopa.selfcare.mscore.constant.RelationshipState; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionAggregation; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionFilter; import it.pagopa.selfcare.mscore.model.onboarding.OnboardedProduct; @@ -63,6 +64,7 @@ public interface UserConnector { List findByInstitutionId(String institutionId); - List getUserInfo(String userId, String institutionId, String[] states); + List getUserInfo(String userId, String institutionId, String[] states); + List countUsers(); } diff --git a/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/aggregation/QueryCount.java b/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/aggregation/QueryCount.java new file mode 100644 index 000000000..09a23b24f --- /dev/null +++ b/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/aggregation/QueryCount.java @@ -0,0 +1,15 @@ +package it.pagopa.selfcare.mscore.model.aggregation; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldNameConstants; + +@Data +@FieldNameConstants(asEnum = true) +@AllArgsConstructor +@NoArgsConstructor +public class QueryCount { + private String _id; + private Integer count; +} diff --git a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImpl.java b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImpl.java index a900979dd..b7f1d052c 100644 --- a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImpl.java +++ b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImpl.java @@ -1,6 +1,5 @@ package it.pagopa.selfcare.mscore.connector.dao; -import com.mongodb.client.model.Facet; import it.pagopa.selfcare.mscore.api.DelegationConnector; import it.pagopa.selfcare.mscore.connector.dao.model.DelegationEntity; import it.pagopa.selfcare.mscore.connector.dao.model.mapper.DelegationEntityMapper; @@ -21,10 +20,7 @@ import org.springframework.stereotype.Component; import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -122,7 +118,7 @@ public Delegation findByIdAndModifyStatus(String delegationId, DelegationState s @Override public boolean checkIfDelegationsAreActive(String institutionId) { - Optional opt = repository.findByToAndStatus(institutionId, DelegationState.ACTIVE); - return opt.isPresent(); + List opt = repository.findByToAndStatus(institutionId, DelegationState.ACTIVE).orElse(Collections.emptyList()); + return !opt.isEmpty(); } } diff --git a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationRepository.java b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationRepository.java index 3e3160d09..13dfb2146 100644 --- a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationRepository.java +++ b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/DelegationRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -13,5 +14,5 @@ public interface DelegationRepository extends MongoRepository findByFromAndToAndProductIdAndType(String from, String to, String productId, DelegationType type); - Optional findByToAndStatus(String to, DelegationState status); + Optional> findByToAndStatus(String to, DelegationState status); } diff --git a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImpl.java b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImpl.java index e9740a423..135ecde1d 100644 --- a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImpl.java +++ b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImpl.java @@ -13,6 +13,7 @@ import it.pagopa.selfcare.mscore.constant.RelationshipState; import it.pagopa.selfcare.mscore.exception.InvalidRequestException; import it.pagopa.selfcare.mscore.exception.ResourceNotFoundException; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionAggregation; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionFilter; import it.pagopa.selfcare.mscore.model.onboarding.OnboardedProduct; @@ -28,10 +29,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.FindAndModifyOptions; import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.aggregation.GraphLookupOperation; -import org.springframework.data.mongodb.core.aggregation.MatchOperation; -import org.springframework.data.mongodb.core.aggregation.UnwindOperation; +import org.springframework.data.mongodb.core.aggregation.*; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; @@ -45,6 +43,7 @@ import java.util.stream.Collectors; import static it.pagopa.selfcare.mscore.constant.CustomError.*; +import static org.springframework.data.mongodb.core.aggregation.Aggregation.group; @Slf4j @Component @@ -436,4 +435,14 @@ private String constructQuery(String... variables) { Arrays.stream(variables).forEach(s -> builder.append(".").append(s)); return builder.toString(); } + + @Override + public List countUsers() { + UnwindOperation unwindBindings = Aggregation.unwind("$bindings", "binding", true); + UnwindOperation unwindProducts = Aggregation.unwind("$bindings.products", "products", true); + MatchOperation matchStatusId = Aggregation.match(Criteria.where("bindings.products.status").in(VALID_USER_RELATIONSHIPS)); + GroupOperation productCount = group("bindings.products.productId").count().as("count"); + Aggregation aggregation = Aggregation.newAggregation(unwindBindings, unwindProducts, matchStatusId, productCount); + return mongoOperations.aggregate(aggregation, "User", QueryCount.class).getMappedResults(); + } } diff --git a/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImplTest.java b/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImplTest.java index eea0e834b..546f2dee1 100644 --- a/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImplTest.java +++ b/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/DelegationConnectorImplTest.java @@ -27,7 +27,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.test.context.ContextConfiguration; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -218,12 +218,19 @@ void findByIdAndModifyStatus() { } @Test - void checkIfDelegationsAreActive() { - when(delegationRepository.findByToAndStatus(anyString(), any())).thenReturn(Optional.of(new DelegationEntity())); + void checkIfDelegationsAreActive_true() { + when(delegationRepository.findByToAndStatus(anyString(), any())).thenReturn(Optional.of(List.of(new DelegationEntity()))); boolean response = delegationConnectorImpl.checkIfDelegationsAreActive("id"); assertTrue(response); } + @Test + void checkIfDelegationsAreActive_false() { + when(delegationRepository.findByToAndStatus(anyString(), any())).thenReturn(Optional.of(Collections.emptyList())); + boolean response = delegationConnectorImpl.checkIfDelegationsAreActive("id"); + assertFalse(response); + } + @Test void find_shouldGetDataPaginated() { diff --git a/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImplTest.java b/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImplTest.java index d4d043f05..059d82c1a 100644 --- a/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImplTest.java +++ b/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/UserConnectorImplTest.java @@ -14,6 +14,7 @@ import it.pagopa.selfcare.mscore.constant.Env; import it.pagopa.selfcare.mscore.constant.RelationshipState; import it.pagopa.selfcare.mscore.exception.ResourceNotFoundException; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionAggregation; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionBinding; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionFilter; @@ -1159,4 +1160,30 @@ void findUsersByInstitutionIdAndProductId(){ List userIds = userConnectorImpl.findUsersByInstitutionIdAndProductId("institutionId", "productId"); assertEquals(1, userIds.size()); } + + + @Test + void countUser() { + //Given + AggregationResults results = mock(AggregationResults.class); + + when(results.getMappedResults()).thenReturn(List.of( + new QueryCount("prod1", 1), + new QueryCount("prod2", 2), + new QueryCount("prod3", 3))); + + //When + when(mongoTemplate.aggregate(any(Aggregation.class), anyString(), any())). + thenReturn(results); + + List response = userConnectorImpl.countUsers(); + + //Then + assertNotNull(response); + assertFalse(response.isEmpty()); + QueryCount actual = response.get(0); + + assertEquals(actual.getCount(), 1); + } + } diff --git a/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationService.java b/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationService.java index addb20d22..e458110de 100644 --- a/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationService.java +++ b/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationService.java @@ -1,5 +1,6 @@ package it.pagopa.selfcare.mscore.core; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import org.springframework.scheduling.annotation.Async; import java.util.List; @@ -13,4 +14,6 @@ public interface QueueNotificationService { void sendContracts(Optional size, List productsFilter); void sendUsers(Optional size, Optional page, List productsFilter, Optional userId); + + List countUsers(); } diff --git a/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceImpl.java b/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceImpl.java index c1d165329..c6b91d839 100644 --- a/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceImpl.java +++ b/core/src/main/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceImpl.java @@ -6,6 +6,7 @@ import it.pagopa.selfcare.mscore.constant.RelationshipState; import it.pagopa.selfcare.mscore.exception.ResourceNotFoundException; import it.pagopa.selfcare.mscore.model.QueueEvent; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import it.pagopa.selfcare.mscore.model.institution.Institution; import it.pagopa.selfcare.mscore.model.institution.Onboarding; import it.pagopa.selfcare.mscore.model.onboarding.OnboardedUser; @@ -19,7 +20,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; @Service @Slf4j @@ -184,4 +184,9 @@ public void sendUsers(Optional size, Optional page, List countUsers() { + return userConnector.countUsers(); + } } diff --git a/core/src/test/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceTest.java b/core/src/test/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceTest.java index 24e405a58..9ec6f916d 100644 --- a/core/src/test/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceTest.java +++ b/core/src/test/java/it/pagopa/selfcare/mscore/core/QueueNotificationServiceTest.java @@ -6,6 +6,7 @@ import it.pagopa.selfcare.mscore.constant.RelationshipState; import it.pagopa.selfcare.mscore.exception.ResourceNotFoundException; import it.pagopa.selfcare.mscore.model.QueueEvent; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import it.pagopa.selfcare.mscore.model.institution.Institution; import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; import it.pagopa.selfcare.mscore.model.institution.Onboarding; @@ -24,7 +25,8 @@ import java.util.Optional; import static it.pagopa.selfcare.commons.utils.TestUtils.mockInstance; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -232,4 +234,27 @@ void startScheduler_emptyProductList(){ assertDoesNotThrow(executable); verifyNoInteractions(userEventService); } + + @Test + void countUser(){ + //when + Executable executable = () -> schedulerService.countUsers(); + //then + assertDoesNotThrow(executable); + verify(userConnector, times(1)).countUsers(); + } + + @Test + void countUserMock() { + List prodCount = new ArrayList<>(); + prodCount.add(new QueryCount("prod1", 1)); + prodCount.add(new QueryCount("prod2", 2)); + prodCount.add(new QueryCount("prod3", 3)); + when(userConnector.countUsers()).thenReturn(prodCount); + List responseCount = schedulerService.countUsers(); + assertNotNull(responseCount); + assertFalse(responseCount.isEmpty()); + assertEquals(3, responseCount.size()); + assertEquals(2, responseCount.get(1).getCount()); + } } diff --git a/helm/pnpg/values-uat.yaml b/helm/pnpg/values-uat.yaml index 391853978..82ec3dd8c 100644 --- a/helm/pnpg/values-uat.yaml +++ b/helm/pnpg/values-uat.yaml @@ -1,3 +1,5 @@ +replicaCount: 0 + image: repository: selcucommonacr.azurecr.io/selfcaremscore tag: 1.0-SNAPSHOT @@ -17,8 +19,8 @@ ingress: autoscaling: enabled: false - minReplicas: 1 - maxReplicas: 100 + minReplicas: 0 + maxReplicas: 0 targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index ee54a022b..f17f4f011 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -1,3 +1,5 @@ +replicaCount: 0 + image: repository: selcuacr.azurecr.io/selfcaremscore tag: 1.0-SNAPSHOT @@ -25,8 +27,8 @@ resources: autoscaling: enabled: false - minReplicas: 1 - maxReplicas: 100 + minReplicas: 0 + maxReplicas: 0 targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 diff --git a/infra/container_apps/env/dev/terraform.tfvars b/infra/container_apps/env/dev/terraform.tfvars index 2528c7474..a2d2993fa 100644 --- a/infra/container_apps/env/dev/terraform.tfvars +++ b/infra/container_apps/env/dev/terraform.tfvars @@ -127,7 +127,7 @@ app_settings = [ }, { name = "STORAGE_CONTAINER" - value = "$web" + value = "selc-d-contracts-blob" }, { name = "STORAGE_ENDPOINT" @@ -141,7 +141,7 @@ app_settings = [ { name = "STORAGE_CREDENTIAL_ID" - value = "selcdcheckoutsa" + value = "selcdcontractsstorage" }, { name = "STORAGE_TEMPLATE_URL" diff --git a/infra/container_apps/env/prod/terraform.tfvars b/infra/container_apps/env/prod/terraform.tfvars index 66a57c8e7..c47f731fa 100644 --- a/infra/container_apps/env/prod/terraform.tfvars +++ b/infra/container_apps/env/prod/terraform.tfvars @@ -31,6 +31,10 @@ container_app = { app_settings = [ + { + name = "ONBOARDING_SEND_EMAIL_TO_INSTITUTION" + value = "true" + }, { name = "MAIL_TEMPLATE_COMPLETE_PATH" value = "contracts/template/mail/onboarding-complete/1.0.0.json" @@ -127,7 +131,7 @@ app_settings = [ }, { name = "STORAGE_CONTAINER" - value = "$web" + value = "selc-p-contracts-blob" }, { name = "STORAGE_ENDPOINT" @@ -141,7 +145,7 @@ app_settings = [ { name = "STORAGE_CREDENTIAL_ID" - value = "selcpcheckoutsa" + value = "selcpcontractsstorage" }, { name = "STORAGE_TEMPLATE_URL" diff --git a/infra/container_apps/env/uat-pnpg/terraform.tfvars b/infra/container_apps/env/uat-pnpg/terraform.tfvars index eb48d3e15..ceb32b9ca 100644 --- a/infra/container_apps/env/uat-pnpg/terraform.tfvars +++ b/infra/container_apps/env/uat-pnpg/terraform.tfvars @@ -181,11 +181,11 @@ app_settings = [ }, { name = "MS_NOTIFICATION_MANAGER_URL" - value = "https://selc-u-pnpg-notification-mngr-ca.redground-be752d1f5.westeurope.azurecontainerapps.io" + value = "https://selc-u-pnpg-notification-mngr-ca.redground-be752d1f.westeurope.azurecontainerapps.io" }, { name = "USERVICE_PARTY_REGISTRY_PROXY_URL" - value = "https://selc-u-pnpg-party-reg-proxy-ca.redground-be752d1f5.westeurope.azurecontainerapps.io" + value = "https://selc-u-pnpg-party-reg-proxy-ca.redground-be752d1f.westeurope.azurecontainerapps.io" }, { name = "MS_PRODUCT_URL" diff --git a/infra/container_apps/env/uat/terraform.tfvars b/infra/container_apps/env/uat/terraform.tfvars index ea33acb39..6c2561e6e 100644 --- a/infra/container_apps/env/uat/terraform.tfvars +++ b/infra/container_apps/env/uat/terraform.tfvars @@ -119,7 +119,7 @@ app_settings = [ }, { name = "STORAGE_CONTAINER" - value = "$web" + value = "selc-u-contracts-blob" }, { name = "STORAGE_ENDPOINT" @@ -133,7 +133,7 @@ app_settings = [ { name = "STORAGE_CREDENTIAL_ID" - value = "selcucheckoutsa" + value = "selcucontractsstorage" }, { name = "STORAGE_TEMPLATE_URL" diff --git a/web/src/main/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationController.java b/web/src/main/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationController.java index 145396a47..30416173c 100644 --- a/web/src/main/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationController.java +++ b/web/src/main/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationController.java @@ -3,8 +3,12 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import it.pagopa.selfcare.mscore.core.QueueNotificationService; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; +import it.pagopa.selfcare.mscore.web.model.mapper.UserMapper; +import it.pagopa.selfcare.mscore.web.model.user.ProductCountResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -18,8 +22,11 @@ public class QueueNotificationController { private final QueueNotificationService queueNotificationService; - public QueueNotificationController(QueueNotificationService queueNotificationService) { + private final UserMapper userMapper; + + public QueueNotificationController(QueueNotificationService queueNotificationService, UserMapper userMapper) { this.queueNotificationService = queueNotificationService; + this.userMapper = userMapper; } @ApiOperation(value = "", notes = "${swagger.ms-core.notification-event.api.start}") @PostMapping(value = "/contracts") @@ -50,4 +57,18 @@ public void resendUsers(@RequestParam(name = "size", required = false)Optional countUsers(){ + log.trace("Count users start"); + List counts = queueNotificationService.countUsers(); + + ProductCountResponse productCountResponse = new ProductCountResponse(counts.stream() + .map(userMapper::toProductCount).toList()); + + log.trace("Count users end"); + return ResponseEntity.ok().body(productCountResponse); + } } \ No newline at end of file diff --git a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/UserMapper.java b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/UserMapper.java index 85ebed564..74c259b89 100644 --- a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/UserMapper.java +++ b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/UserMapper.java @@ -2,6 +2,7 @@ import it.pagopa.selfcare.mscore.model.UserNotificationToSend; +import it.pagopa.selfcare.mscore.model.aggregation.QueryCount; import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; import it.pagopa.selfcare.mscore.model.institution.WorkContact; import it.pagopa.selfcare.mscore.model.onboarding.OnboardedUser; @@ -43,6 +44,8 @@ public interface UserMapper { UserNotificationResponse toUserNotification(UserNotificationToSend user); UserProductsResponse toOnboardedUserResponse(OnboardedUser onboardedUser); + @Mapping(source = "_id", target = "productId") + ProductCount toProductCount(QueryCount queryCount); @Named("retrieveMailFromWorkContacts") default String retrieveMailFromWorkContacts(Map map, String institutionId){ diff --git a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/user/ProductCount.java b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/user/ProductCount.java new file mode 100644 index 000000000..feac00ae9 --- /dev/null +++ b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/user/ProductCount.java @@ -0,0 +1,9 @@ +package it.pagopa.selfcare.mscore.web.model.user; + +import lombok.Data; + +@Data +public class ProductCount { + private String productId; + private Integer count; +} diff --git a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/user/ProductCountResponse.java b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/user/ProductCountResponse.java new file mode 100644 index 000000000..274dd99c6 --- /dev/null +++ b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/user/ProductCountResponse.java @@ -0,0 +1,16 @@ +package it.pagopa.selfcare.mscore.web.model.user; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProductCountResponse { + List products; +} diff --git a/web/src/main/resources/swagger/swagger_en.properties b/web/src/main/resources/swagger/swagger_en.properties index e287f0eae..7f1364f96 100644 --- a/web/src/main/resources/swagger/swagger_en.properties +++ b/web/src/main/resources/swagger/swagger_en.properties @@ -9,6 +9,7 @@ swagger.mscore.external.institution.relationships=returns the relationships rela swagger.mscore.institutions=Gets institutions filtering by taxCode and/or subunitCode swagger.ms-core.notification-event.api.start=Service to resend contract notifications on SC-Contracts topic swagger.ms-core.notification-event.api.start.users=Service to resend old user onboardings to the SCUsers kafka queue, it can send the onboardings of a single user or also retrieve all the users for a given set of products in a paged manner by passing page and size +swagger.ms-core.notification-event.api.start.users.count=Users' Count for single product swagger.mscore.institution.create.from-ipa=create an institution from ipa registry swagger.mscore.institution.create.from-ivass=create an institution from ivass CSV swagger.mscore.institution.create.from-infocamere=create an institution from infocamere registry diff --git a/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationControllerTest.java b/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationControllerTest.java index 491011f5a..957bea596 100644 --- a/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationControllerTest.java +++ b/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/QueueNotificationControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import it.pagopa.selfcare.mscore.core.QueueNotificationService; +import it.pagopa.selfcare.mscore.web.model.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; @@ -23,6 +24,10 @@ class QueueNotificationControllerTest { private static final String BASE_URL = "/notification-event"; @Autowired protected MockMvc mvc; + + @MockBean + private UserMapper userMapper; + @MockBean private QueueNotificationService queueNotificationService; @@ -34,7 +39,7 @@ void sendContracts() throws Exception { Integer size = 1; String productId = "product"; mvc.perform(MockMvcRequestBuilders - .post(BASE_URL+"/contracts") + .post(BASE_URL + "/contracts") .param("size", String.valueOf(size)) .param("productsFilter", productId)) .andExpect(status().isOk()); @@ -48,7 +53,7 @@ void sendContractsByInstitutionIdAndTokenId() throws Exception { String institutionId = "institutionId"; String tokenId = "tokenId"; mvc.perform(MockMvcRequestBuilders - .put(BASE_URL+"/contracts") + .put(BASE_URL + "/contracts") .param("tokenId", tokenId) .param("institutionId", institutionId)) .andExpect(status().isOk()); @@ -57,12 +62,12 @@ void sendContractsByInstitutionIdAndTokenId() throws Exception { } @Test - void sendUsers() throws Exception{ + void sendUsers() throws Exception { Integer size = 1; Integer page = 0; String productId = "product"; mvc.perform(MockMvcRequestBuilders - .post(BASE_URL+"/users") + .post(BASE_URL + "/users") .param("size", String.valueOf(size)) .param("page",String.valueOf(page)) .param("productsFilter", productId)) @@ -70,4 +75,10 @@ void sendUsers() throws Exception{ Mockito.verify(queueNotificationService, Mockito.times(1)).sendUsers(Optional.of(size),Optional.of(page), List.of(productId), Optional.empty()); } + + @Test + void countUsers() throws Exception { + mvc.perform(MockMvcRequestBuilders.get(BASE_URL + "/users/count")).andExpect(status().isOk()); + Mockito.verify(queueNotificationService, Mockito.times(1)).countUsers(); + } } \ No newline at end of file