Skip to content

Commit

Permalink
[SELC-4847] Added retry in UserRegistry API
Browse files Browse the repository at this point in the history
  • Loading branch information
flaminiaScarciofolo committed May 30, 2024
1 parent 6f31113 commit 5dea2a9
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import io.smallrye.mutiny.Uni;
import it.pagopa.selfcare.user.model.UpdateUserRequest;
import it.pagopa.selfcare.user.model.notification.UserNotificationToSend;
import org.openapi.quarkus.user_registry_json.model.MutableUserFieldsDto;
import org.openapi.quarkus.user_registry_json.model.*;

import java.util.List;

public interface UserRegistryService {
Uni<List<UserNotificationToSend>> updateUserRegistryAndSendNotificationToQueue(UpdateUserRequest updateUserRequest, String userId, String institutionId);
Uni<UserResource> findByIdUsingGET( String fl, String id);
Uni<UserId> saveUsingPATCH(SaveUserDto saveUserDto);
Uni<UserResource> searchUsingPOST(String fl, UserSearchDto userSearchDto);
Uni<jakarta.ws.rs.core.Response> updateUsingPATCH(String id, MutableUserFieldsDto mutableUserFieldsDto );

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@
import it.pagopa.selfcare.user.util.UserUtils;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.gradle.internal.impldep.org.apache.commons.lang.StringUtils;

import java.time.Duration;
import java.util.*;

import org.openapi.quarkus.user_registry_json.api.UserApi;
import org.openapi.quarkus.user_registry_json.model.UserResource;
import org.openapi.quarkus.user_registry_json.model.*;

import static it.pagopa.selfcare.user.constant.CollectionUtil.MAIL_ID_PREFIX;

Expand All @@ -35,11 +38,44 @@ public class UserRegistryServiceImpl implements UserRegistryService {
private final UserNotificationService userNotificationService;
private final UserMapper userMapper;

@ConfigProperty(name = "user-ms.retry.min-backoff")
Integer retryMinBackOff;

@ConfigProperty(name = "user-ms.retry.max-backoff")
Integer retryMaxBackOff;

@ConfigProperty(name = "user-ms.retry")
Integer maxRetry;


@RestClient
@Inject
private UserApi userRegistryApi;

@Override
public Uni<UserResource> findByIdUsingGET(String fl, String id) {
return userRegistryApi.findByIdUsingGET(fl, id)
.onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofHours(retryMaxBackOff)).atMost(maxRetry);
}

@Override
public Uni<UserId> saveUsingPATCH(SaveUserDto saveUserDto) {
return userRegistryApi.saveUsingPATCH(saveUserDto)
.onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofHours(retryMaxBackOff)).atMost(maxRetry);
}

@Override
public Uni<UserResource> searchUsingPOST(String fl, UserSearchDto userSearchDto) {
return userRegistryApi.searchUsingPOST(fl, userSearchDto)
.onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofHours(retryMaxBackOff)).atMost(maxRetry);
}

@Override
public Uni<Response> updateUsingPATCH(String id, MutableUserFieldsDto mutableUserFieldsDto) {
return userRegistryApi.updateUsingPATCH(id, mutableUserFieldsDto)
.onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofHours(retryMaxBackOff)).atMost(maxRetry);
}


@Override
public Uni<List<UserNotificationToSend>> updateUserRegistryAndSendNotificationToQueue(UpdateUserRequest updateUserRequest, String userId, String institutionId) {
Expand Down Expand Up @@ -91,7 +127,7 @@ private Uni<String> findMailUuidAndUpdateUserRegistry(UserResource userResource,
.map(Map.Entry::getKey);
}

return userRegistryApi.updateUsingPATCH(userResource.getId().toString(),
return updateUsingPATCH(userResource.getId().toString(),
userMapper.toMutableUserFieldsDto(userDto, userResource, mailAlreadyPresent.isPresent() ? null : idMail))
.replaceWith(mailAlreadyPresent.orElse(idMail));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

@RestClient
@Inject
private UserApi userRegistryApi;
private UserRegistryService userRegistryService;

private final UserMapper userMapper;
private final UserInstitutionMapper userInstitutionMapper;
Expand Down Expand Up @@ -109,7 +107,7 @@ public Uni<List<String>> getUsersEmails(String institutionId, String productId)
var productFilters = OnboardedProductFilter.builder().productId(productId).status(ACTIVE).build().constructMap();
Multi<UserInstitution> userInstitutions = userInstitutionService.findAllWithFilter(userUtils.retrieveMapForFilter(userInstitutionFilters, productFilters));
return userInstitutions
.onItem().transformToUni(userInstitution -> userRegistryApi.findByIdUsingGET(WORK_CONTACTS, userInstitution.getUserId())
.onItem().transformToUni(userInstitution -> userRegistryService.findByIdUsingGET(WORK_CONTACTS, userInstitution.getUserId())
.map(userResource -> Objects.nonNull(userResource.getWorkContacts()) && userResource.getWorkContacts().containsKey(userInstitution.getUserMailUuid())
? userResource.getWorkContacts().get(userInstitution.getUserMailUuid()) : null))
.merge()
Expand All @@ -123,7 +121,7 @@ public Uni<List<String>> getUsersEmails(String institutionId, String productId)
public Multi<UserProductResponse> getUserProductsByInstitution(String institutionId) {
Multi<UserInstitution> userInstitutions = UserInstitution.find(UserInstitution.Fields.institutionId.name(), institutionId).stream();
return userInstitutions.onItem()
.transformToUni(userInstitution -> userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
.transformToUni(userInstitution -> userRegistryService.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
.map(userResource -> UserProductResponse.builder()
.id(userResource.getId().toString())
.name(userResource.getName().getValue())
Expand All @@ -145,7 +143,7 @@ public Uni<UserResource> retrievePerson(String userId, String productId, String
log.error(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId));
return new ResourceNotFoundException(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId), USER_NOT_FOUND_ERROR.getCode());
})
.onItem().transformToUni(userInstitution -> userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId()))
.onItem().transformToUni(userInstitution -> userRegistryService.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId()))
.onFailure(UserUtils::checkIfNotFoundException).transform(t -> new ResourceNotFoundException(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId), USER_NOT_FOUND_ERROR.getCode()));
}

Expand Down Expand Up @@ -220,14 +218,14 @@ public Uni<UserDetailResponse> getUserById(String userId, String institutionId,
log.error(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId));
return new UserInstitution();
})
.onItem().transformToUni(userInstitution -> userRegistryApi.findByIdUsingGET(fields, userId)
.onItem().transformToUni(userInstitution -> userRegistryService.findByIdUsingGET(fields, userId)
.map(userResource -> userMapper.toUserDetailResponse(userResource, Optional.ofNullable(institutionId).map(ignored -> userInstitution.getUserMailUuid()).orElse(null))))
.onFailure(UserUtils::checkIfNotFoundException).transform(t -> new ResourceNotFoundException(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId), USER_NOT_FOUND_ERROR.getCode()));
}

@Override
public Uni<UserDetailResponse> searchUserByFiscalCode(String fiscalCode, String institutionId) {
Uni<UserResource> userResourceUni = userRegistryApi.searchUsingPOST(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, new UserSearchDto(fiscalCode))
Uni<UserResource> userResourceUni = userRegistryService.searchUsingPOST(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, new UserSearchDto(fiscalCode))
.onFailure(UserUtils::checkIfNotFoundException).transform(t -> new ResourceNotFoundException("User not found", USER_NOT_FOUND_ERROR.getCode()));

return userResourceUni
Expand Down Expand Up @@ -294,7 +292,7 @@ private Uni<PrepareNotificationData.PrepareNotificationDataBuilder> retrieveUser
}

private Uni<PrepareNotificationData.PrepareNotificationDataBuilder> retrieveUserFromUserRegistryAndAddToPrepareNotificationData(PrepareNotificationData.PrepareNotificationDataBuilder prepareNotificationDataBuilder, String userId) {
return userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userId)
return userRegistryService.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userId)
.onItem().transform(prepareNotificationDataBuilder::userResource);
}

Expand All @@ -307,7 +305,7 @@ public Uni<List<UserNotificationToSend>> findPaginatedUserNotificationToSend(Int
queryParameter = OnboardedProductFilter.builder().status(VALID_USER_PRODUCT_STATES_FOR_NOTIFICATION).build().constructMap();
}
return userInstitutionService.paginatedFindAllWithFilter(queryParameter, page, size)
.onItem().transformToUniAndMerge(userInstitution -> userRegistryApi
.onItem().transformToUniAndMerge(userInstitution -> userRegistryService
.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())
.map(userResource -> userUtils.buildUsersNotificationResponse(userInstitution, userResource, productId)))
.collect().in(ArrayList::new, List::addAll);
Expand All @@ -331,7 +329,7 @@ public Uni<UserInfo> retrieveBindings(String institutionId, String userId, Strin

/**
* The createOrUpdateUserByFiscalCode method is a method that either creates a new user or updates an existing one based on the provided CreateUserDto object.
* The method starts by calling the searchUsingPOST method on the userRegistryApi object, this is an asynchronous operation that searches for a user in the user registry.
* The method starts by calling the searchUsingPOST method on the userRegistryService object, this is an asynchronous operation that searches for a user in the user registry.
* If the search operation fails with a UserNotFoundException, it recovers by returning a Uni that fails with a ResourceNotFoundException.
* This is a way of transforming one type of exception into another.
* If the search operation is successful, it call method to Update user on userRegistry and UserInstitution collection.
Expand All @@ -342,7 +340,7 @@ public Uni<UserInfo> retrieveBindings(String institutionId, String userId, Strin
*/
@Override
public Uni<String> createOrUpdateUserByFiscalCode(CreateUserDto userDto, LoggedUser loggedUser) {
return userRegistryApi.searchUsingPOST(USERS_WORKS_FIELD_LIST, new UserSearchDto(userDto.getUser().getFiscalCode()))
return userRegistryService.searchUsingPOST(USERS_WORKS_FIELD_LIST, new UserSearchDto(userDto.getUser().getFiscalCode()))
.onFailure(UserUtils::isUserNotFoundExceptionOnUserRegistry).recoverWithUni(throwable -> Uni.createFrom().failure(new ResourceNotFoundException(throwable.getMessage())))
.onItem().transformToUni(userResource -> updateUserOnUserRegistryAndUserInstitutionByFiscalCode(userResource, userDto))
.onFailure(ResourceNotFoundException.class).recoverWithUni(throwable -> createUserOnUserRegistryAndUserInstitution(userDto))
Expand Down Expand Up @@ -374,7 +372,7 @@ private Uni<PrepareNotificationData> updateUserOnUserRegistryAndUserInstitutionB
workContactToSave.put(mailUuid, workContact);
userResource.setWorkContacts(workContactToSave);

return userRegistryApi.updateUsingPATCH(userResource.getId().toString(), userMapper.toMutableUserFieldsDto(userResource))
return userRegistryService.updateUsingPATCH(userResource.getId().toString(), userMapper.toMutableUserFieldsDto(userResource))
.onFailure().invoke(exception -> log.error("Error during update user on userRegistry: {} ", exception.getMessage(), exception))
.onItem().invoke(response -> log.info("User with id {} updated on userRegistry", userResource.getId()))
.onItem().transformToUni(response -> userInstitutionService.findByUserIdAndInstitutionId(userResource.getId().toString(), userDto.getInstitutionId()))
Expand Down Expand Up @@ -402,14 +400,14 @@ private Uni<PrepareNotificationData> createUserOnUserRegistryAndUserInstitution(
String mailUuid = randomMailId.get();
Map<String, WorkContactResource> workContacts = new HashMap<>();
workContacts.put(mailUuid, UserUtils.buildWorkContact(userDto.getUser().getInstitutionEmail()));
return userRegistryApi.saveUsingPATCH(userMapper.toSaveUserDto(userDto.getUser(), workContacts))
return userRegistryService.saveUsingPATCH(userMapper.toSaveUserDto(userDto.getUser(), workContacts))
.onFailure().invoke(exception -> log.error("Error during create user on userRegistry: {} ", exception.getMessage(), exception))
.onItem().invoke(userResource -> log.info("User created with id {}", userResource.getId()))
.onItem().transform(userResource -> userResource.getId().toString())
.onItem().transform(userId -> updateOrCreateUserInstitution(userDto, mailUuid, null, userId))
.onItem().invoke(userInstitution -> log.info("start persist userInstititon: {}", userInstitution))
.onItem().transformToUni(userInstitutionService::persistOrUpdate)
.onItem().transformToUni(userInstitution -> userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
.onItem().transformToUni(userInstitution -> userRegistryService.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
.map(resource -> PrepareNotificationData.builder().userResource(resource).userInstitution(userInstitution).queueEvent(QueueEvent.ADD)))
.onItem().transformToUni(prepareNotificationDataBuilder -> retrieveProductAndAddToPrepareNotificationData(prepareNotificationDataBuilder, userDto.getProduct().getProductId()))
.map(PrepareNotificationData.PrepareNotificationDataBuilder::build)
Expand All @@ -419,14 +417,14 @@ private Uni<PrepareNotificationData> createUserOnUserRegistryAndUserInstitution(

/**
* The createOrUpdateUserByUserId method is a method that either add to existingUser a new user Role.
* The method starts by calling the findByIdUsingGET method on the userRegistryApi object,
* The method starts by calling the findByIdUsingGET method on the userRegistryService object,
* If the search operation fails with a UserNotFoundException, it recovers by returning a Uni that fails with a ResourceNotFoundException.
* If the search operation is successful, it call method to Update user on UserInstitution collection.
* Finally, if any operation fails, it logs an error message and returns a Uni that emits a failure.
*/
@Override
public Uni<Void> createOrUpdateUserByUserId(AddUserRoleDto userDto, String userId, LoggedUser loggedUser) {
return userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userId)
return userRegistryService.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userId)
.onFailure(UserUtils::isUserNotFoundExceptionOnUserRegistry).recoverWithUni(throwable -> Uni.createFrom().failure(new ResourceNotFoundException(throwable.getMessage())))
.onItem().transformToUni(userResource -> updateUserInstitutionByUserId(userResource, userDto, loggedUser))
.onFailure().invoke(exception -> log.error("Error during retrieve user from userRegistry: {} ", exception.getMessage(), exception));
Expand Down Expand Up @@ -517,7 +515,7 @@ public Multi<UserDataResponse> retrieveUsersData(String institutionId, String pe
.onItem().invoke(userInstitution -> applyFiltersToRemoveProducts(userInstitution, states, products, roles, productRoles))
.onItem().invoke(userInstitution -> log.info("userInstitution found: {}", userInstitution))
.onItem().transformToUniAndMerge(userInstitution ->
userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
userRegistryService.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
.map(userResource -> userMapper.toUserDataResponse(userInstitution, userResource)));
}

Expand Down
4 changes: 4 additions & 0 deletions apps/user-ms/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ user-ms.aws.ses.secret-key=${AWS_SES_SECRET_ACCESS_KEY:secret-key-example}
user-ms.aws.ses.region=${AWS_SES_REGION:eu-south-1}

quarkus.smallrye-openapi.store-schema-directory=src/main/docs

user-ms.retry.min-backoff=${USER-MS-RETRY-MIN-BACKOFF:10}
user-ms.retry.max-backoff=${USER-MS-RETRY-MAX-BACKOFF:12}
user-ms.retry=${USER-MS-RETRY:3}
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ class UserServiceTest {
@InjectMock
private UserInfoService userInfoService;

@RestClient
@InjectMock
private UserApi userRegistryApi;
private UserRegistryService userRegistryApi;

@InjectMock
private UserMapper userMapper;
Expand Down
14 changes: 13 additions & 1 deletion infra/container_apps/user-ms/env/dev-pnpg/terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,19 @@ app_settings = [
{
name = "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC"
value = "string"
}
},
{
name = "USER-MS-RETRY-MIN-BACKOFF"
value = 10
},
{
name = "USER-MS-RETRY-MAX-BACKOFF"
value = 12
},
{
name = "USER-MS-RETRY"
value = 3
}
]

secrets_names = {
Expand Down
12 changes: 12 additions & 0 deletions infra/container_apps/user-ms/env/dev/terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ app_settings = [
{
name = "USER_MS_EVENTHUB_USERS_ENABLED"
value = true
},
{
name = "USER-MS-RETRY-MIN-BACKOFF"
value = 10
},
{
name = "USER-MS-RETRY-MAX-BACKOFF"
value = 12
},
{
name = "USER-MS-RETRY"
value = 3
}
]

Expand Down
12 changes: 12 additions & 0 deletions infra/container_apps/user-ms/env/prod-pnpg/terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ app_settings = [
{
name = "USER_REGISTRY_URL"
value = "https://api.pdv.pagopa.it/user-registry/v1"
},
{
name = "USER-MS-RETRY-MIN-BACKOFF"
value = 10
},
{
name = "USER-MS-RETRY-MAX-BACKOFF"
value = 12
},
{
name = "USER-MS-RETRY"
value = 3
}
]

Expand Down
12 changes: 12 additions & 0 deletions infra/container_apps/user-ms/env/prod/terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ app_settings = [
{
name = "EVENT_HUB_BASE_PATH"
value = "https://selc-p-eventhub-ns.servicebus.windows.net/sc-users"
},
{
name = "USER-MS-RETRY-MIN-BACKOFF"
value = 10
},
{
name = "USER-MS-RETRY-MAX-BACKOFF"
value = 12
},
{
name = "USER-MS-RETRY"
value = 3
}
]

Expand Down
12 changes: 12 additions & 0 deletions infra/container_apps/user-ms/env/uat-pnpg/terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ app_settings = [
{
name = "STORAGE_CONTAINER_PRODUCT"
value = "selc-u-product"
},
{
name = "USER-MS-RETRY-MIN-BACKOFF"
value = 10
},
{
name = "USER-MS-RETRY-MAX-BACKOFF"
value = 12
},
{
name = "USER-MS-RETRY"
value = 3
}
]

Expand Down
12 changes: 12 additions & 0 deletions infra/container_apps/user-ms/env/uat/terraform.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ app_settings = [
{
name = "STORAGE_CONTAINER_PRODUCT"
value = "selc-u-product"
},
{
name = "USER-MS-RETRY-MIN-BACKOFF"
value = 10
},
{
name = "USER-MS-RETRY-MAX-BACKOFF"
value = 12
},
{
name = "USER-MS-RETRY"
value = 3
}

]
Expand Down

0 comments on commit 5dea2a9

Please sign in to comment.