From bfaaadbe4950e0d3c9c7dc3c7696d51bc8414299 Mon Sep 17 00:00:00 2001 From: Giulia Tremolada <124147597+giulia-tremolada@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:48:34 +0200 Subject: [PATCH] [SELC-4968] feat: add delegation update and userInstitution update during institutionUpdate (#502) Co-authored-by: manuraf --- app/src/main/resources/swagger/api-docs.json | 6 + .../mscore/api/DelegationConnector.java | 2 + .../selfcare/mscore/api/UserApiConnector.java | 4 + .../mscore/constant/GenericError.java | 2 + .../model/institution/InstitutionUpdate.java | 3 +- .../dao/DelegationConnectorImpl.java | 24 ++++ .../connector/dao/MongoCustomConnector.java | 3 + .../dao/MongoCustomConnectorImpl.java | 11 +- .../model/mapper/InstitutionMapperHelper.java | 17 ++- .../dao/DelegationConnectorImplTest.java | 41 +++++++ .../dao/MongoCustomConnectorImplTest.java | 10 ++ .../rest/docs/openapi/selfcare-user-docs.json | 72 +++++++++-- .../connector/rest/UserApiConnectorImpl.java | 16 ++- .../client/UserInstitutionApiRestClient.java | 8 ++ .../UserInstitutionApiRestClientConfig.java | 15 +++ .../rest/mapper/UserMapperClient.java | 6 + .../config/user-rest-client.properties | 7 +- .../rest/UserApiConnectorImplTest.java | 40 +++++++ .../mscore/core/InstitutionServiceImpl.java | 46 ++++++- .../core/InstitutionServiceImplTest.java | 113 +++++++++++++++++- .../web/model/institution/InstitutionPut.java | 1 + .../model/mapper/InstitutionMapperCustom.java | 1 + .../controller/InstitutionControllerTest.java | 1 + 23 files changed, 425 insertions(+), 24 deletions(-) create mode 100644 connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/client/UserInstitutionApiRestClient.java create mode 100644 connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/config/UserInstitutionApiRestClientConfig.java diff --git a/app/src/main/resources/swagger/api-docs.json b/app/src/main/resources/swagger/api-docs.json index 91700c485..1bc300068 100644 --- a/app/src/main/resources/swagger/api-docs.json +++ b/app/src/main/resources/swagger/api-docs.json @@ -3233,6 +3233,9 @@ "items" : { "type" : "string" } + }, + "parentDescription" : { + "type" : "string" } } }, @@ -3514,6 +3517,9 @@ "ivassCode" : { "type" : "string" }, + "parentDescription" : { + "type" : "string" + }, "paymentServiceProvider" : { "$ref" : "#/components/schemas/PaymentServiceProvider" }, diff --git a/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/DelegationConnector.java b/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/DelegationConnector.java index 21e8d2d1a..3f2eff6ee 100644 --- a/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/DelegationConnector.java +++ b/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/DelegationConnector.java @@ -5,6 +5,7 @@ import it.pagopa.selfcare.mscore.model.delegation.Delegation; import it.pagopa.selfcare.mscore.model.delegation.DelegationWithPagination; import it.pagopa.selfcare.mscore.model.delegation.GetDelegationParameters; +import it.pagopa.selfcare.mscore.model.institution.Institution; import java.util.List; @@ -17,4 +18,5 @@ public interface DelegationConnector { Delegation findByIdAndModifyStatus(String delegationId, DelegationState status); boolean checkIfDelegationsAreActive(String institutionId); Delegation findAndActivate(String from, String to, String productId); + void updateDelegation(Institution update); } diff --git a/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserApiConnector.java b/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserApiConnector.java index 5cbbb7a14..383081cd9 100644 --- a/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserApiConnector.java +++ b/connector-api/src/main/java/it/pagopa/selfcare/mscore/api/UserApiConnector.java @@ -1,10 +1,14 @@ package it.pagopa.selfcare.mscore.api; +import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; + import java.util.List; public interface UserApiConnector { List getUserEmails(String institutionId, String productId); + void updateUserInstitution(String institutionId, InstitutionUpdate institutionUpdate); + } diff --git a/connector-api/src/main/java/it/pagopa/selfcare/mscore/constant/GenericError.java b/connector-api/src/main/java/it/pagopa/selfcare/mscore/constant/GenericError.java index 8bf41265d..e106525da 100644 --- a/connector-api/src/main/java/it/pagopa/selfcare/mscore/constant/GenericError.java +++ b/connector-api/src/main/java/it/pagopa/selfcare/mscore/constant/GenericError.java @@ -13,6 +13,7 @@ public enum GenericError { CREATE_DELEGATION_ERROR("001-0027", "Error while creating requested delegation"), SEND_MAIL_FOR_DELEGATION_ERROR("002-0027", "Error during send mail for delegation"), DELETE_DELEGATION_ERROR("0003-0027", "Error while deleting delegation"), + UPDATE_DELEGATION_ERROR("004-0027", "Error while updating delegation"), ONBOARDING_VERIFICATION_ERROR("0015", "Error while verifying onboarding"), GETTING_ONBOARDING_INFO_ERROR("0016", "Error while getting onboarding info"), GET_PRODUCTS_ERROR("0031", "Error while getting products"), @@ -59,6 +60,7 @@ public enum GenericError { GET_INSTITUTIONS_REQUEST_ERROR("0054", "Invalid request parameters sent. Allowed filters combinations taxCode and subunit or origin and originId"), VERIFY_USER_ERROR("0000", "Error while searching institutions related to given productId"), GET_USER_ERROR("0000", "Error while searching user given UserID"), + UPDATE_USER_INSTITUTION_ERROR("0000", "Error while updating InstitutionUser for id %s"), GENERIC_ERROR("0000", "Generic Error"); private final String code; private final String detail; diff --git a/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/institution/InstitutionUpdate.java b/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/institution/InstitutionUpdate.java index 6315a4d94..902bfb713 100644 --- a/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/institution/InstitutionUpdate.java +++ b/connector-api/src/main/java/it/pagopa/selfcare/mscore/model/institution/InstitutionUpdate.java @@ -31,5 +31,6 @@ public class InstitutionUpdate { private String ivassCode; private boolean imported; private AdditionalInformations additionalInformations; - private boolean delegation; + private Boolean delegation; + private String parentDescription; } 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 65b39b10f..9aaa08a9e 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 @@ -9,6 +9,7 @@ import it.pagopa.selfcare.mscore.model.delegation.DelegationWithPagination; import it.pagopa.selfcare.mscore.model.delegation.GetDelegationParameters; import it.pagopa.selfcare.mscore.model.delegation.PageInfo; +import it.pagopa.selfcare.mscore.model.institution.Institution; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -153,4 +154,27 @@ public Delegation findAndActivate(String from, String to, String productId) { FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().upsert(false).returnNew(true); return delegationMapper.convertToDelegation(repository.findAndModify(query, update, findAndModifyOptions, DelegationEntity.class)); } + + @Override + public void updateDelegation(Institution institutionUpdate) { + + // If institution own some delegations, we also update "to" reference + // isDelegation is true if institution own some delegations + if (institutionUpdate.isDelegation()) { + Update updateFrom = new Update(); + Query queryFrom = Query.query(Criteria.where(DelegationEntity.Fields.to.name()).is(institutionUpdate.getId())); + updateFrom.set(DelegationEntity.Fields.institutionToName.name(), institutionUpdate.getDescription()); + repository.updateMulti(queryFrom, updateFrom, DelegationEntity.class); + } + + Update updateTo = new Update(); + Query queryTo = Query.query(Criteria.where(DelegationEntity.Fields.from.name()).is(institutionUpdate.getId())); + updateTo.set(DelegationEntity.Fields.institutionFromName.name(), institutionUpdate.getDescription()); + if (Objects.nonNull(institutionUpdate.getParentDescription())) { + updateTo.set(DelegationEntity.Fields.institutionFromRootName.name(), institutionUpdate.getParentDescription()); + } + repository.updateMulti(queryTo, updateTo, DelegationEntity.class); + + } + } diff --git a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnector.java b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnector.java index d4b4c8576..5fbb442f5 100644 --- a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnector.java +++ b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnector.java @@ -1,5 +1,6 @@ package it.pagopa.selfcare.mscore.connector.dao; +import com.mongodb.client.result.UpdateResult; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionFilter; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -22,4 +23,6 @@ public interface MongoCustomConnector { O findAndModify(Query query, UpdateDefinition updateDefinition, FindAndModifyOptions findAndModifyOptions, Class outputType); List findUserInstitutionAggregation(UserInstitutionFilter filter, Class outputType); + + UpdateResult updateMulti(Query query, UpdateDefinition updateDefinition, Class outputType); } diff --git a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImpl.java b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImpl.java index 6f7e642ec..a9c352af3 100644 --- a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImpl.java +++ b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImpl.java @@ -1,12 +1,16 @@ package it.pagopa.selfcare.mscore.connector.dao; +import com.mongodb.client.result.UpdateResult; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionFilter; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; 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.*; +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.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.UpdateDefinition; @@ -95,4 +99,9 @@ private MatchOperation checkIfInstitutionIdIsPresent(UserInstitutionFilter filte private void checkIfExternalIdIsPresent(UserInstitutionFilter filter, GraphLookupOperation.GraphLookupOperationBuilder graphLookupOperation) { graphLookupOperation.restrict(Criteria.where("externalId").is(filter.getExternalId())); } + + @Override + public UpdateResult updateMulti(Query query, UpdateDefinition updateDefinition, Class outputType){ + return mongoOperations.updateMulti(query, updateDefinition, outputType); + } } diff --git a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/model/mapper/InstitutionMapperHelper.java b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/model/mapper/InstitutionMapperHelper.java index 4ee04277d..88543fcdd 100644 --- a/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/model/mapper/InstitutionMapperHelper.java +++ b/connector/dao/src/main/java/it/pagopa/selfcare/mscore/connector/dao/model/mapper/InstitutionMapperHelper.java @@ -1,15 +1,19 @@ package it.pagopa.selfcare.mscore.connector.dao.model.mapper; import it.pagopa.selfcare.mscore.connector.dao.model.InstitutionEntity; -import it.pagopa.selfcare.mscore.connector.dao.model.inner.*; -import it.pagopa.selfcare.mscore.constant.Origin; -import it.pagopa.selfcare.mscore.model.institution.*; +import it.pagopa.selfcare.mscore.connector.dao.model.inner.GeoTaxonomyEntity; +import it.pagopa.selfcare.mscore.model.institution.DataProtectionOfficer; +import it.pagopa.selfcare.mscore.model.institution.InstitutionGeographicTaxonomies; +import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; +import it.pagopa.selfcare.mscore.model.institution.PaymentServiceProvider; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.springframework.data.mongodb.core.query.Update; -import java.time.OffsetDateTime; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; @NoArgsConstructor(access = AccessLevel.NONE) @@ -34,6 +38,7 @@ public static Map getNotNullField(InstitutionUpdate institutionU response.put(InstitutionUpdate.Fields.institutionType.name(), institutionUpdate.getInstitutionType().name()); } response.put(InstitutionUpdate.Fields.description.name(), institutionUpdate.getDescription()); + response.put(InstitutionUpdate.Fields.parentDescription.name(), institutionUpdate.getParentDescription()); response.put(InstitutionUpdate.Fields.digitalAddress.name(), institutionUpdate.getDigitalAddress()); response.put(InstitutionUpdate.Fields.address.name(), institutionUpdate.getAddress()); response.put(InstitutionUpdate.Fields.taxCode.name(), institutionUpdate.getTaxCode()); @@ -44,7 +49,7 @@ public static Map getNotNullField(InstitutionUpdate institutionU response.put(InstitutionUpdate.Fields.supportEmail.name(), institutionUpdate.getSupportEmail()); response.put(InstitutionUpdate.Fields.supportPhone.name(), institutionUpdate.getSupportPhone()); response.put(InstitutionUpdate.Fields.imported.name(), institutionUpdate.isImported()); - response.put(InstitutionUpdate.Fields.delegation.name(), institutionUpdate.isDelegation()); + response.put(InstitutionUpdate.Fields.delegation.name(), institutionUpdate.getDelegation()); if(institutionUpdate.getPaymentServiceProvider() != null) { response.put(constructPaymentInnerField(PaymentServiceProvider.Fields.abiCode.name()), 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 459270dc5..142f59184 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 @@ -12,8 +12,10 @@ import it.pagopa.selfcare.mscore.exception.MsCoreException; import it.pagopa.selfcare.mscore.model.delegation.*; import it.pagopa.selfcare.mscore.model.institution.Institution; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -306,4 +308,43 @@ private GetDelegationParameters createDelegationParameters(String from, String t .size(size) .build(); } + + @Test + void updateDelegation() { + + String description = "description"; + String rootName = "rootName"; + String institutionId = "institutionId"; + + Institution institutionUpdate = new Institution(); + institutionUpdate.setId(institutionId); + institutionUpdate.setDescription(description); + institutionUpdate.setParentDescription(rootName); + institutionUpdate.setDelegation(true); + + //when + final Executable executable = () -> delegationConnectorImpl.updateDelegation(institutionUpdate); + + Assertions.assertDoesNotThrow(executable); + verify(delegationRepository, times(2)).updateMulti(any(), any(), any()); + + } + + @Test + void updateDelegation_noDelegation() { + + String description = "description"; + String institutionId = "institutionId"; + + Institution institutionUpdate = new Institution(); + institutionUpdate.setId(institutionId); + institutionUpdate.setDescription(description); + + //when + final Executable executable = () -> delegationConnectorImpl.updateDelegation(institutionUpdate); + + Assertions.assertDoesNotThrow(executable); + verify(delegationRepository, times(1)).updateMulti(any(), any(), any()); + + } } diff --git a/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImplTest.java b/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImplTest.java index 3bca43e62..79a58c66e 100644 --- a/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImplTest.java +++ b/connector/dao/src/test/java/it/pagopa/selfcare/mscore/connector/dao/MongoCustomConnectorImplTest.java @@ -1,5 +1,6 @@ package it.pagopa.selfcare.mscore.connector.dao; +import com.mongodb.client.result.UpdateResult; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionAggregation; import it.pagopa.selfcare.mscore.model.aggregation.UserInstitutionFilter; import org.junit.jupiter.api.Assertions; @@ -125,5 +126,14 @@ void findUserAndInstitutionWithExternalId() { } + @Test + void updateMulti() { + UpdateResult updateResult = mock(UpdateResult.class); + when(mongoOperations.updateMulti(any(), any(), (Class) any())).thenReturn(updateResult); + Query query = new Query(); + UpdateDefinition updateDefinition = new Update(); + assertNotNull(mongoCustomConnector.updateMulti(query, updateDefinition, Object.class)); + } + } diff --git a/connector/rest/docs/openapi/selfcare-user-docs.json b/connector/rest/docs/openapi/selfcare-user-docs.json index 0aceba1c4..2cbbda3a5 100644 --- a/connector/rest/docs/openapi/selfcare-user-docs.json +++ b/connector/rest/docs/openapi/selfcare-user-docs.json @@ -1,7 +1,7 @@ { "openapi" : "3.0.3", "info" : { - "title" : "user-ms API", + "title" : "User API", "version" : "1.0.0" }, "servers" : [ { @@ -12,16 +12,14 @@ "description" : "Auto generated value" } ], "paths" : { - "/authorize/{institutionId}" : { + "/authorize" : { "get" : { "tags" : [ "User Permission Controller" ], "summary" : "Get permission for a user in an institution", "parameters" : [ { "name" : "institutionId", - "in" : "path", - "required" : true, + "in" : "query", "schema" : { - "minLength" : 1, "type" : "string" } }, { @@ -61,6 +59,46 @@ } ] } }, + "/institutions/{institutionId}" : { + "put" : { + "tags" : [ "Institution Controller" ], + "summary" : "The API updates the description in all occurrences of userInstitution, given a certain institutionId.", + "parameters" : [ { + "name" : "institutionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UpdateDescriptionDto" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { } + } + }, + "403" : { + "description" : "Not Allowed" + }, + "401" : { + "description" : "Not Authorized" + } + }, + "security" : [ { + "SecurityScheme" : [ ] + } ] + } + }, "/institutions/{institutionId}/products/{productId}/createdAt" : { "put" : { "tags" : [ "Institution Controller" ], @@ -660,6 +698,12 @@ "schema" : { "type" : "string" } + }, { + "name" : "productRole", + "in" : "query", + "schema" : { + "type" : "string" + } }, { "name" : "status", "in" : "query", @@ -1129,7 +1173,7 @@ "type" : "string" }, "Product" : { - "required" : [ "productId", "role" ], + "required" : [ "productId", "role", "productRoles" ], "type" : "object", "properties" : { "productId" : { @@ -1151,7 +1195,7 @@ } }, "Product1" : { - "required" : [ "productId", "role" ], + "required" : [ "productId", "role", "productRoles" ], "type" : "object", "properties" : { "productId" : { @@ -1185,7 +1229,21 @@ } } }, + "UpdateDescriptionDto" : { + "required" : [ "institutionDescription" ], + "type" : "object", + "properties" : { + "institutionDescription" : { + "minLength" : 1, + "type" : "string" + }, + "institutionRootName" : { + "type" : "string" + } + } + }, "UpdateUserRequest" : { + "required" : [ "email" ], "type" : "object", "properties" : { "name" : { diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImpl.java b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImpl.java index 569edfeb1..0abeec951 100644 --- a/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImpl.java +++ b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImpl.java @@ -2,6 +2,9 @@ import it.pagopa.selfcare.mscore.api.UserApiConnector; import it.pagopa.selfcare.mscore.connector.rest.client.UserApiRestClient; +import it.pagopa.selfcare.mscore.connector.rest.client.UserInstitutionApiRestClient; +import it.pagopa.selfcare.mscore.connector.rest.mapper.UserMapperClient; +import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -14,8 +17,14 @@ public class UserApiConnectorImpl implements UserApiConnector { private final UserApiRestClient userApiRestClient; - public UserApiConnectorImpl(UserApiRestClient userApiRestClient) { + private final UserInstitutionApiRestClient userInstitutionApiRestClient; + + private final UserMapperClient userMapper; + + public UserApiConnectorImpl(UserApiRestClient userApiRestClient, UserInstitutionApiRestClient userInstitutionApiRestClient, UserMapperClient userMapper) { this.userApiRestClient = userApiRestClient; + this.userInstitutionApiRestClient = userInstitutionApiRestClient; + this.userMapper = userMapper; } @Override @@ -24,4 +33,9 @@ public List getUserEmails(String institutionId, String productId){ return userEmails.getBody(); } + @Override + public void updateUserInstitution(String institutionId, InstitutionUpdate institutionUpdate){ + userInstitutionApiRestClient._institutionsInstitutionIdPut(institutionId, userMapper.toUpdateDescriptionDto(institutionUpdate)); + } + } diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/client/UserInstitutionApiRestClient.java b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/client/UserInstitutionApiRestClient.java new file mode 100644 index 000000000..e1608dd73 --- /dev/null +++ b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/client/UserInstitutionApiRestClient.java @@ -0,0 +1,8 @@ +package it.pagopa.selfcare.mscore.connector.rest.client; + +import it.pagopa.selfcare.user.generated.openapi.v1.api.InstitutionControllerApi; +import org.springframework.cloud.openfeign.FeignClient; + +@FeignClient(name = "${rest-client.user-ms.institution.serviceCode}", url = "${rest-client.user-ms.base-url}") +public interface UserInstitutionApiRestClient extends InstitutionControllerApi { +} diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/config/UserInstitutionApiRestClientConfig.java b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/config/UserInstitutionApiRestClientConfig.java new file mode 100644 index 000000000..f07eac28c --- /dev/null +++ b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/config/UserInstitutionApiRestClientConfig.java @@ -0,0 +1,15 @@ +package it.pagopa.selfcare.mscore.connector.rest.config; + +import it.pagopa.selfcare.commons.connector.rest.config.RestClientBaseConfig; +import it.pagopa.selfcare.mscore.connector.rest.client.UserInstitutionApiRestClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@Import(RestClientBaseConfig.class) +@EnableFeignClients(clients = UserInstitutionApiRestClient.class) +@PropertySource("classpath:config/user-rest-client.properties") +public class UserInstitutionApiRestClientConfig { +} diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/mapper/UserMapperClient.java b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/mapper/UserMapperClient.java index 4e8fd4164..ea1361915 100644 --- a/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/mapper/UserMapperClient.java +++ b/connector/rest/src/main/java/it/pagopa/selfcare/mscore/connector/rest/mapper/UserMapperClient.java @@ -1,6 +1,8 @@ package it.pagopa.selfcare.mscore.connector.rest.mapper; +import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; import it.pagopa.selfcare.mscore.model.user.User; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.UpdateDescriptionDto; import it.pagopa.selfcare.user_registry.generated.openapi.v1.dto.UserId; import it.pagopa.selfcare.user_registry.generated.openapi.v1.dto.UserResource; import org.mapstruct.Mapper; @@ -17,4 +19,8 @@ public interface UserMapperClient { @Mapping(target = "id", source = "userId.id") User fromUserId (UserId userId); + @Mapping(target = "institutionDescription", source = "description") + @Mapping(target = "institutionRootName", source = "parentDescription") + UpdateDescriptionDto toUpdateDescriptionDto (InstitutionUpdate institutionUpdate); + } diff --git a/connector/rest/src/main/resources/config/user-rest-client.properties b/connector/rest/src/main/resources/config/user-rest-client.properties index 6731874f0..b15e4980c 100644 --- a/connector/rest/src/main/resources/config/user-rest-client.properties +++ b/connector/rest/src/main/resources/config/user-rest-client.properties @@ -6,4 +6,9 @@ feign.client.config.user-ms.requestInterceptors[0]=it.pagopa.selfcare.commons.co feign.client.config.user-ms.connectTimeout=${SELFCARE_USER_REST_CLIENT_CONNECT_TIMEOUT:${REST_CLIENT_CONNECT_TIMEOUT:5000}} feign.client.config.user-ms.readTimeout=${SELFCARE_USER_REST_CLIENT_READ_TIMEOUT:${REST_CLIENT_READ_TIMEOUT:5000}} feign.client.config.user-ms.loggerLevel=${SELFCARE_USER_REST_CLIENT_LOGGER_LEVEL:${REST_CLIENT_LOGGER_LEVEL:FULL}} -feign.client.config.user-ms.errorDecoder=it.pagopa.selfcare.mscore.connector.rest.decoder.FeignErrorDecoder \ No newline at end of file +feign.client.config.user-ms.errorDecoder=it.pagopa.selfcare.mscore.connector.rest.decoder.FeignErrorDecoder +feign.client.config.user-institution-ms.requestInterceptors[0]=it.pagopa.selfcare.commons.connector.rest.interceptor.AuthorizationHeaderInterceptor +feign.client.config.user-institution-ms.connectTimeout=${SELFCARE_USER_REST_CLIENT_CONNECT_TIMEOUT:${REST_CLIENT_CONNECT_TIMEOUT:5000}} +feign.client.config.user-institution-ms.readTimeout=${SELFCARE_USER_REST_CLIENT_READ_TIMEOUT:${REST_CLIENT_READ_TIMEOUT:5000}} +feign.client.config.user-institution-ms.loggerLevel=${SELFCARE_USER_REST_CLIENT_LOGGER_LEVEL:${REST_CLIENT_LOGGER_LEVEL:FULL}} +feign.client.config.user-institution-ms.errorDecoder=it.pagopa.selfcare.mscore.connector.rest.decoder.FeignErrorDecoder diff --git a/connector/rest/src/test/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImplTest.java b/connector/rest/src/test/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImplTest.java index b675d57ce..aba11a202 100644 --- a/connector/rest/src/test/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImplTest.java +++ b/connector/rest/src/test/java/it/pagopa/selfcare/mscore/connector/rest/UserApiConnectorImplTest.java @@ -1,8 +1,14 @@ package it.pagopa.selfcare.mscore.connector.rest; import it.pagopa.selfcare.mscore.connector.rest.client.UserApiRestClient; +import it.pagopa.selfcare.mscore.connector.rest.client.UserInstitutionApiRestClient; +import it.pagopa.selfcare.mscore.connector.rest.mapper.UserMapperClient; +import it.pagopa.selfcare.mscore.model.institution.InstitutionUpdate; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.UpdateDescriptionDto; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; @@ -26,6 +32,12 @@ class UserApiConnectorImplTest { @MockBean private UserApiRestClient userApiRestClient; + @MockBean + private UserInstitutionApiRestClient userInstitutionApiRestClient; + + @MockBean + private UserMapperClient userMapperClient; + @Test void getUserEmailsByInstitutionAndProduct(){ //given @@ -42,4 +54,32 @@ void getUserEmailsByInstitutionAndProduct(){ verify(userApiRestClient, times(1))._usersEmailsGet(institutionId, productId); } + + @Test + void updateUserInstitution(){ + //given + final String institutionId = "institutionId"; + final String description = "description"; + final String rootName = "rootName"; + + UpdateDescriptionDto descriptionDto = new UpdateDescriptionDto(); + descriptionDto.setInstitutionDescription(description); + descriptionDto.setInstitutionRootName(rootName); + + InstitutionUpdate institutionUpdate = new InstitutionUpdate(); + institutionUpdate.setDescription(description); + institutionUpdate.setParentDescription(rootName); + + when(userMapperClient.toUpdateDescriptionDto(any())).thenReturn(descriptionDto); + + //when + final Executable executable = () -> userApiConnector.updateUserInstitution(institutionId, institutionUpdate); + + //then + Assertions.assertDoesNotThrow(executable); + verify(userInstitutionApiRestClient)._institutionsInstitutionIdPut(institutionId, descriptionDto); + } + + + } \ No newline at end of file diff --git a/core/src/main/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImpl.java b/core/src/main/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImpl.java index a230c3a82..c9674eec6 100644 --- a/core/src/main/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImpl.java +++ b/core/src/main/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImpl.java @@ -2,8 +2,10 @@ import it.pagopa.selfcare.commons.base.security.SelfCareUser; import it.pagopa.selfcare.commons.base.utils.InstitutionType; +import it.pagopa.selfcare.mscore.api.DelegationConnector; import it.pagopa.selfcare.mscore.api.InstitutionConnector; import it.pagopa.selfcare.mscore.api.PartyRegistryProxyConnector; +import it.pagopa.selfcare.mscore.api.UserApiConnector; import it.pagopa.selfcare.mscore.config.CoreConfig; import it.pagopa.selfcare.mscore.constant.*; import it.pagopa.selfcare.mscore.core.mapper.InstitutionMapper; @@ -20,17 +22,15 @@ import it.pagopa.selfcare.mscore.model.institution.*; import it.pagopa.selfcare.mscore.model.onboarding.Token; import lombok.extern.slf4j.Slf4j; +import org.owasp.encoder.Encode; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; -import static it.pagopa.selfcare.mscore.constant.GenericError.CREATE_INSTITUTION_ERROR; +import static it.pagopa.selfcare.mscore.constant.GenericError.*; @Slf4j @Service @@ -39,6 +39,8 @@ public class InstitutionServiceImpl implements InstitutionService { private static final String REQUIRED_INSTITUTION_MESSAGE = "An institution id is required"; private final InstitutionConnector institutionConnector; + private final UserApiConnector userApiConnector; + private final DelegationConnector delegationConnector; private final PartyRegistryProxyConnector partyRegistryProxyConnector; private final CoreConfig coreConfig; private final ContractEventNotificationService contractService; @@ -48,6 +50,7 @@ public class InstitutionServiceImpl implements InstitutionService { public InstitutionServiceImpl(PartyRegistryProxyConnector partyRegistryProxyConnector, InstitutionConnector institutionConnector, + UserApiConnector userApiConnector, DelegationConnector delegationConnector, CoreConfig coreConfig, ContractEventNotificationService contractService, InstitutionMapper institutionMapper, @@ -55,6 +58,8 @@ public InstitutionServiceImpl(PartyRegistryProxyConnector partyRegistryProxyConn TokenMapper tokenMapper) { this.partyRegistryProxyConnector = partyRegistryProxyConnector; this.institutionConnector = institutionConnector; + this.userApiConnector = userApiConnector; + this.delegationConnector = delegationConnector; this.coreConfig = coreConfig; this.contractService = contractService; this.institutionMapper = institutionMapper; @@ -304,10 +309,39 @@ public Optional retrieveGeoTaxonomies(String code) { @Override public Institution updateInstitution(String institutionId, InstitutionUpdate institutionUpdate, String userId) { + Institution outdatedInstitution = institutionConnector.findById(institutionId); + List geographicTaxonomies = retrieveGeographicTaxonomies(institutionUpdate); - return institutionConnector.findAndUpdate(institutionId, null, geographicTaxonomies, institutionUpdate); + Institution updatedInstitution = institutionConnector.findAndUpdate(institutionId, null, geographicTaxonomies, institutionUpdate); + + if(Objects.nonNull(institutionUpdate.getDescription())) { + try { + delegationConnector.updateDelegation(updatedInstitution); + } catch (Exception e) { + log.error(UPDATE_DELEGATION_ERROR.getMessage() + ":", e.getMessage(), e); + rollbackInstitution(outdatedInstitution); + throw new MsCoreException(PUT_INSTITUTION_ERROR.getMessage(), PUT_INSTITUTION_ERROR.getCode()); + } + try { + userApiConnector.updateUserInstitution(institutionId, institutionUpdate); + } catch (Exception e) { + log.error(String.format(UPDATE_USER_INSTITUTION_ERROR.getMessage(), Encode.forJava(institutionId)) + ":", e.getMessage(), e); + rollbackInstitution(outdatedInstitution); + delegationConnector.updateDelegation(outdatedInstitution); + throw new MsCoreException(PUT_INSTITUTION_ERROR.getMessage(), PUT_INSTITUTION_ERROR.getCode()); + } + } + return updatedInstitution; } + private void rollbackInstitution(Institution institution) { + InstitutionUpdate institutionUpdate = new InstitutionUpdate(); + institutionUpdate.setDescription(institution.getDescription()); + institutionUpdate.setParentDescription(institution.getParentDescription()); + institutionConnector.findAndUpdate(institution.getId(), null, null, institutionUpdate); + } + + @Override public void updateInstitutionDelegation(String institutionId, boolean delegation) { InstitutionUpdate institutionUpdate = new InstitutionUpdate(); diff --git a/core/src/test/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImplTest.java b/core/src/test/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImplTest.java index 07817f3f9..6bbae5e71 100644 --- a/core/src/test/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImplTest.java +++ b/core/src/test/java/it/pagopa/selfcare/mscore/core/InstitutionServiceImplTest.java @@ -3,8 +3,10 @@ import it.pagopa.selfcare.commons.base.security.PartyRole; import it.pagopa.selfcare.commons.base.security.SelfCareUser; import it.pagopa.selfcare.commons.base.utils.InstitutionType; +import it.pagopa.selfcare.mscore.api.DelegationConnector; import it.pagopa.selfcare.mscore.api.InstitutionConnector; import it.pagopa.selfcare.mscore.api.PartyRegistryProxyConnector; +import it.pagopa.selfcare.mscore.api.UserApiConnector; import it.pagopa.selfcare.mscore.config.CoreConfig; import it.pagopa.selfcare.mscore.constant.RelationshipState; import it.pagopa.selfcare.mscore.constant.SearchMode; @@ -48,6 +50,12 @@ class InstitutionServiceImplTest { @Mock private InstitutionConnector institutionConnector; + @Mock + private DelegationConnector delegationConnector; + + @Mock + private UserApiConnector userApiConnector; + @InjectMocks private InstitutionServiceImpl institutionServiceImpl; @@ -843,8 +851,111 @@ void testRetrieveInstitutionsWithFilter6() { @Test void testUpdateInstitutionDescription() { + //given + final String institutionId = "id"; + + Institution updatedInstitution = new Institution(); + updatedInstitution.setId(institutionId); + updatedInstitution.setDescription("newDesc"); + updatedInstitution.setParentDescription("newRootName"); + + InstitutionUpdate institutionUpdate = new InstitutionUpdate(); + institutionUpdate.setDescription("newDesc"); + institutionUpdate.setParentDescription("newRootName"); + + //when + when(institutionConnector.findById(institutionId)).thenReturn(new Institution()); + when(institutionConnector.findAndUpdate(institutionId, null, Collections.emptyList(), institutionUpdate)).thenReturn(updatedInstitution); + + //then + Institution result = institutionServiceImpl.updateInstitution(institutionId, institutionUpdate, "userId"); + verify(delegationConnector).updateDelegation(updatedInstitution); + verify(userApiConnector).updateUserInstitution(institutionId, institutionUpdate); + assertEquals(result, updatedInstitution); + } + + @Test + void testUpdateInstitutionDescription_updateDelegationsFails() { + //given + final String institutionId = "id"; + + Institution outdatedInstitution = new Institution(); + outdatedInstitution.setId(institutionId); + outdatedInstitution.setDescription("oldDesc"); + outdatedInstitution.setParentDescription("oldRootName"); + + Institution updatedInstitution = new Institution(); + updatedInstitution.setId(institutionId); + updatedInstitution.setDescription("newDesc"); + updatedInstitution.setParentDescription("newRootName"); + + InstitutionUpdate institutionUpdate = new InstitutionUpdate(); + institutionUpdate.setDescription("newDesc"); + institutionUpdate.setParentDescription("newRootName"); + + InstitutionUpdate institutionRollback = new InstitutionUpdate(); + institutionRollback.setDescription("oldDesc"); + institutionRollback.setParentDescription("oldRootName"); + + //when + when(institutionConnector.findById(institutionId)).thenReturn(outdatedInstitution); + when(institutionConnector.findAndUpdate(institutionId, null, Collections.emptyList(), institutionUpdate)).thenReturn(updatedInstitution); + doThrow(new RuntimeException()).when(delegationConnector).updateDelegation(updatedInstitution); + + Executable executable = () -> institutionServiceImpl.updateInstitution(institutionId, institutionUpdate, "userId"); + // Then + assertThrows(MsCoreException.class, executable); + verify(institutionConnector).findAndUpdate(institutionId,null, null, institutionRollback); + verifyNoInteractions(userApiConnector); + } + + @Test + void testUpdateInstitutionDescription_updateUserInstitutionFails() { + //given + final String institutionId = "id"; + + Institution outdatedInstitution = new Institution(); + outdatedInstitution.setId(institutionId); + outdatedInstitution.setDescription("oldDesc"); + outdatedInstitution.setParentDescription("oldRootName"); + + Institution updatedInstitution = new Institution(); + updatedInstitution.setId(institutionId); + updatedInstitution.setDescription("newDesc"); + updatedInstitution.setParentDescription("newRootName"); + + InstitutionUpdate institutionUpdate = new InstitutionUpdate(); + institutionUpdate.setDescription("newDesc"); + institutionUpdate.setParentDescription("newRootName"); + + InstitutionUpdate institutionRollback = new InstitutionUpdate(); + institutionRollback.setDescription("oldDesc"); + institutionRollback.setParentDescription("oldRootName"); + + //when + when(institutionConnector.findById(institutionId)).thenReturn(outdatedInstitution); + when(institutionConnector.findAndUpdate(institutionId, null, Collections.emptyList(), institutionUpdate)).thenReturn(updatedInstitution); + doThrow(new RuntimeException()).when(userApiConnector).updateUserInstitution(institutionId, institutionUpdate); + + Executable executable = () -> institutionServiceImpl.updateInstitution(institutionId, institutionUpdate, "userId"); + + //then + assertThrows(MsCoreException.class, executable); + verify(institutionConnector).findAndUpdate(institutionId,null, null, institutionRollback); + verify(delegationConnector).updateDelegation(outdatedInstitution); + } + + @Test + void updateInstitution_noDescription(){ + //when + when(institutionConnector.findById(any())).thenReturn(new Institution()); when(institutionConnector.findAndUpdate(any(), any(), any(), any())).thenReturn(new Institution()); - assertDoesNotThrow(() -> institutionServiceImpl.updateInstitution("42", new InstitutionUpdate(), "userId")); + + institutionServiceImpl.updateInstitution("id", new InstitutionUpdate(), "userId"); + + // Then + verifyNoInteractions(delegationConnector); + verifyNoInteractions(userApiConnector); } @Test diff --git a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/institution/InstitutionPut.java b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/institution/InstitutionPut.java index b2040a19f..31d9c1e9f 100644 --- a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/institution/InstitutionPut.java +++ b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/institution/InstitutionPut.java @@ -12,4 +12,5 @@ public class InstitutionPut { List geographicTaxonomyCodes; private String digitalAddress; private String description; + private String parentDescription; } diff --git a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/InstitutionMapperCustom.java b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/InstitutionMapperCustom.java index 8b041dcda..9ced076f2 100644 --- a/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/InstitutionMapperCustom.java +++ b/web/src/main/java/it/pagopa/selfcare/mscore/web/model/mapper/InstitutionMapperCustom.java @@ -140,6 +140,7 @@ public static InstitutionUpdate toInstitutionUpdate(InstitutionPut institutionPu InstitutionUpdate institutionUpdate = new InstitutionUpdate(); institutionUpdate.setDescription(institutionPut.getDescription()); institutionUpdate.setDigitalAddress(institutionPut.getDigitalAddress()); + institutionUpdate.setParentDescription(institutionPut.getParentDescription()); institutionUpdate.setGeographicTaxonomies(Optional.ofNullable(institutionPut.getGeographicTaxonomyCodes()) .map(geoTaxonomiesCodes -> geoTaxonomiesCodes.stream() .map(code -> new InstitutionGeographicTaxonomies(code, null)) diff --git a/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/InstitutionControllerTest.java b/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/InstitutionControllerTest.java index ebcb01283..5b9d617dd 100644 --- a/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/InstitutionControllerTest.java +++ b/web/src/test/java/it/pagopa/selfcare/mscore/web/controller/InstitutionControllerTest.java @@ -1143,6 +1143,7 @@ void testUpdateInstitutionDescription() throws Exception { InstitutionPut pgInstitutionPut = new InstitutionPut(); pgInstitutionPut.setDescription("desc"); pgInstitutionPut.setDigitalAddress("digitalAddress"); + pgInstitutionPut.setParentDescription("parentDesc"); when(institutionService.updateInstitution(any(), any(), any())).thenReturn(new Institution()); MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.put("/institutions/42") .contentType(MediaType.APPLICATION_JSON)