Skip to content

Commit

Permalink
Merge pull request #24 from pagopa/feature/SELC-3903
Browse files Browse the repository at this point in the history
[SELC-3903] Added api for Update user status given userId and optionalFilter
  • Loading branch information
flaminiaScarciofolo authored Jan 29, 2024
2 parents 5e9aad8 + 53cdca4 commit 343c8a6
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

import io.quarkus.security.Authenticated;
import io.smallrye.mutiny.Uni;
import it.pagopa.selfcare.onboarding.common.PartyRole;
import it.pagopa.selfcare.user.constant.OnboardedProductState;
import it.pagopa.selfcare.user.controller.response.UserResponse;
import it.pagopa.selfcare.user.mapper.UserMapper;
import it.pagopa.selfcare.user.service.UserEventService;
import it.pagopa.selfcare.user.service.UserService;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.eclipse.microprofile.openapi.annotations.Operation;

import java.util.List;
Expand Down Expand Up @@ -72,5 +76,33 @@ public Uni<Void> deleteProducts(@PathParam(value = "userId") String userId,
@PathParam(value = "productId") String productId) {
return userService.deleteUserInstitutionProduct(userId, institutionId, productId);
}

/**
* The updateUserStatus function updates the status of a user's product.
*
* @param userId String
* @param institutionId String
* @param productId String
* @param role PartyRole
* @param productRole String
* @param status OnboardedProductState
* @return A uni&lt;response&gt;
*/
@Operation(summary = "Update user status with optional filter for institution, product, role and productRole")
@PUT
@Path(value = "/{id}/status")
@Produces(MediaType.APPLICATION_JSON)
public Uni<Response> updateUserStatus(@PathParam(value = "id") String userId,
@QueryParam(value = "institutionId") String institutionId,
@QueryParam(value = "productId") String productId,
@QueryParam(value = "role") PartyRole role,
@QueryParam(value = "productRole") String productRole,
@QueryParam(value = "status") OnboardedProductState status) {
log.debug("updateProductStatus - userId: {}", userId);
return userService.updateUserStatusWithOptionalFilter(userId, institutionId, productId, role, productRole, status)
.map(ignore -> Response
.status(HttpStatus.SC_NO_CONTENT)
.build());
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ public interface UserInstitutionService {
Uni<UserInstitution> retrieveFirstFilteredUserInstitution(Map<String, Object> queryParameter);

Uni<Long> deleteUserInstitutionProduct(String userId, String institutionId, String productId);

Uni<Long> updateUserStatusWithOptionalFilterByInstitutionAndProduct(String userId, String institutionId, String productId, PartyRole role, String productRole, OnboardedProductState status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.quarkus.mongodb.panache.reactive.ReactivePanacheQuery;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import it.pagopa.selfcare.onboarding.common.PartyRole;
import it.pagopa.selfcare.user.constant.OnboardedProductState;
import it.pagopa.selfcare.user.controller.response.UserInstitutionResponse;
import it.pagopa.selfcare.user.entity.OnboardedProduct;
Expand Down Expand Up @@ -76,6 +77,14 @@ public Uni<Long> deleteUserInstitutionProduct(String userId, String institutionI
return updateUserStatusDao(filterMap, OnboardedProductState.DELETED);
}

@Override
public Uni<Long> updateUserStatusWithOptionalFilterByInstitutionAndProduct(String userId, String institutionId, String productId, PartyRole role, String productRole, OnboardedProductState status) {
Map<String, Object> onboardedProductFilterMap = OnboardedProductFilter.builder().productId(productId).role(role).productRole(productRole).build().constructMap();
Map<String, Object> userInstitutionFilterMap = UserInstitutionFilter.builder().userId(userId).institutionId(institutionId).build().constructMap();
Map<String, Object> filterMap = userUtils.retrieveMapForFilter(onboardedProductFilterMap, userInstitutionFilterMap);
return updateUserStatusDao(filterMap, status);
}

@Override
public Multi<UserInstitution> findAllWithFilter(Map<String, Object> queryParameter) {
Document query = queryUtils.buildQueryDocument(queryParameter, USER_INSTITUTION_COLLECTION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,16 @@
import it.pagopa.selfcare.user.controller.response.UserProductResponse;
import org.openapi.quarkus.user_registry_json.model.UserResource;

import io.smallrye.mutiny.Uni;

import java.util.List;


public interface UserService {
Uni<List<String>> getUsersEmails(String institutionId, String productId);
Multi<UserProductResponse> getUserProductsByInstitution(String institutionId);
Uni<UserResource> retrievePerson(String userId, String productId, String institutionId);
Multi<UserInstitutionResponse> findAllUserInstitutions(String institutionId,
String userId,
List<String> roles,
List<String> states,
List<String> products,
List<String> productRoles);

Uni<Void> updateUserStatusWithOptionalFilter(String userId, String institutionId, String productId, PartyRole role, String productRole, OnboardedProductState status);
Multi<UserInstitutionResponse> findAllUserInstitutions(String institutionId, String userId, List<String> roles, List<String> states, List<String> products, List<String> productRoles);
Uni<Void> deleteUserInstitutionProduct(String userId, String institutionId, String productId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,76 @@

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import it.pagopa.selfcare.onboarding.common.PartyRole;
import it.pagopa.selfcare.user.constant.OnboardedProductState;
import it.pagopa.selfcare.user.controller.response.UserInstitutionResponse;
import it.pagopa.selfcare.user.controller.response.UserProductResponse;
import it.pagopa.selfcare.user.entity.UserInstitution;
import it.pagopa.selfcare.user.entity.filter.OnboardedProductFilter;
import it.pagopa.selfcare.user.entity.filter.UserInstitutionFilter;
import it.pagopa.selfcare.user.exception.InvalidRequestException;
import it.pagopa.selfcare.user.exception.ResourceNotFoundException;
import it.pagopa.selfcare.user.mapper.OnboardedProductMapper;
import it.pagopa.selfcare.user.mapper.UserInstitutionMapper;
import it.pagopa.selfcare.user.util.UserUtils;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.reactive.client.api.WebClientApplicationException;
import org.openapi.quarkus.user_registry_json.api.UserApi;
import org.openapi.quarkus.user_registry_json.model.UserResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static it.pagopa.selfcare.user.constant.CustomError.USER_NOT_FOUND_ERROR;
import static it.pagopa.selfcare.user.constant.CustomError.USER_TO_UPDATE_NOT_FOUND;
import static it.pagopa.selfcare.user.util.GeneralUtils.formatQueryParameterList;
import static it.pagopa.selfcare.user.constant.CustomError.*;

@RequiredArgsConstructor
@ApplicationScoped
@Slf4j
public class UserServiceImpl implements UserService {

@RestClient
@Inject
private UserApi userRegistryApi;

private final OnboardedProductMapper onboardedProductMapper;
private final UserInstitutionMapper userInstitutionMapper;
private final UserUtils userUtils;
private final UserInstitutionService userInstitutionService;
private static final String USERS_WORKS_FIELD_LIST = "fiscalCode,familyName,email,name,workContacts";
private final UserInstitutionMapper userInstitutionMapper;

private static final String WORK_CONTACTS = "workContacts";
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

private static final String USERS_WORKS_FIELD_LIST = "fiscalCode,familyName,email,name,workContacts";


/**
* The updateUserStatus function updates the status of a user's onboarded product.
*/
@Override
public Uni<Void> updateUserStatusWithOptionalFilter(String userId, String institutionId, String productId, PartyRole role, String productRole, OnboardedProductState status) {

if (status == null) {
return Uni.createFrom().failure(new InvalidRequestException(STATUS_IS_MANDATORY.getMessage()));
}

userUtils.checkProductRole(productId, role, productRole);

return userInstitutionService.updateUserStatusWithOptionalFilterByInstitutionAndProduct(userId, institutionId, productId, role, productRole, status)
.onItem().transformToUni(aLong -> {
if (aLong < 1) {
return Uni.createFrom().failure(new ResourceNotFoundException(USER_TO_UPDATE_NOT_FOUND.getMessage()));
}
return Uni.createFrom().nullItem();
});
}

@Override
public Uni<List<String>> getUsersEmails(String institutionId, String productId) {
var userInstitutionFilters = constructUserInstitutionFilterMap(institutionId);
var productFilters = constructOnboardedProductFilterMap(productId);
Multi<UserInstitution> userInstitutions = userInstitutionService.findAllWithFilter(retrieveMapForFilter(userInstitutionFilters, productFilters));
var userInstitutionFilters = UserInstitutionFilter.builder().institutionId(institutionId).build().constructMap();
var productFilters = OnboardedProductFilter.builder().productId(productId).build().constructMap();
Multi<UserInstitution> userInstitutions = userInstitutionService.findAllWithFilter(userUtils.retrieveMapForFilter(userInstitutionFilters, productFilters));
return userInstitutions.onItem()
.transformToUni(obj -> userRegistryApi.findByIdUsingGET(WORK_CONTACTS, obj.getUserId()))
.merge()
Expand All @@ -61,7 +85,7 @@ public Uni<List<String>> getUsersEmails(String institutionId, String productId)

@Override
public Multi<UserProductResponse> getUserProductsByInstitution(String institutionId) {
Multi<UserInstitution> userInstitutions = UserInstitution.find("institutionId", institutionId).stream();
Multi<UserInstitution> userInstitutions = UserInstitution.find(UserInstitution.Fields.institutionId.name(), institutionId).stream();
return userInstitutions.onItem()
.transformToUni(userInstitution -> userRegistryApi.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userInstitution.getUserId())
.map(userResource -> UserProductResponse.builder()
Expand All @@ -76,26 +100,23 @@ public Multi<UserProductResponse> getUserProductsByInstitution(String institutio

@Override
public Uni<UserResource> retrievePerson(String userId, String productId, String institutionId) {
Map<String, Object> queryParameter = buildQueryParams(userId, productId, institutionId);
var userInstitutionFilters = UserInstitutionFilter.builder().userId(userId).institutionId(institutionId).build().constructMap();
var productFilters = OnboardedProductFilter.builder().productId(productId).build().constructMap();
Map<String, Object> queryParameter = userUtils.retrieveMapForFilter(userInstitutionFilters, productFilters);
return userInstitutionService.retrieveFirstFilteredUserInstitution(queryParameter)
.onItem().ifNull().failWith(() -> {
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()))
.onFailure(this::checkIfNotFoundException).transform(t -> new ResourceNotFoundException(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId), USER_NOT_FOUND_ERROR.getCode()));
.onFailure(UserUtils::checkIfNotFoundException).transform(t -> new ResourceNotFoundException(String.format(USER_NOT_FOUND_ERROR.getMessage(), userId), USER_NOT_FOUND_ERROR.getCode()));
}

@Override
public Multi<UserInstitutionResponse> findAllUserInstitutions(String institutionId,
String userId,
List<String> roles,
List<String> states,
List<String> products,
List<String> productRoles) {
var userInstitutionFilters = constructUserInstitutionFilterMap(institutionId, userId);
var productFilters = constructOnboardedProductFilterMap(products, states, roles, productRoles);
Multi<UserInstitution> userInstitutions = userInstitutionService.findAllWithFilter(retrieveMapForFilter(userInstitutionFilters, productFilters));
public Multi<UserInstitutionResponse> findAllUserInstitutions(String institutionId, String userId, List<String> roles, List<String> states, List<String> products, List<String> productRoles) {
var userInstitutionFilters = UserInstitutionFilter.builder().userId(userId).institutionId(institutionId).build().constructMap();
var productFilters = OnboardedProductFilter.builder().productId(products).status(states).role(roles).productRole(productRoles).build().constructMap();
Multi<UserInstitution> userInstitutions = userInstitutionService.findAllWithFilter(userUtils.retrieveMapForFilter(userInstitutionFilters, productFilters));
return userInstitutions.onItem().transform(userInstitutionMapper::toResponse);
}

Expand All @@ -110,69 +131,4 @@ public Uni<Void> deleteUserInstitutionProduct(String userId, String institutionI
});
}

private Map<String, Object> buildQueryParams(String userId, String productId, String institutionId) {
OnboardedProductFilter onboardedProductFilter = OnboardedProductFilter.builder()
.productId(productId)
.build();

UserInstitutionFilter userInstitutionFilter = UserInstitutionFilter.builder()
.userId(userId)
.institutionId(institutionId)
.build();

Map<String, Object> filterMap = userInstitutionFilter.constructMap();
filterMap.putAll(onboardedProductFilter.constructMap());
return filterMap;
}

private Map<String, Object> constructUserInstitutionFilterMap(String institutionId, String userId) {
return UserInstitutionFilter
.builder()
.institutionId(institutionId)
.userId(userId)
.build()
.constructMap();
}

private Map<String, Object> constructOnboardedProductFilterMap(List<String> products,
List<String> states,
List<String> roles,
List<String> productRoles) {
return OnboardedProductFilter.builder()
.productId(formatQueryParameterList(products))
.role(formatQueryParameterList(roles))
.status(formatQueryParameterList(states))
.productRole(formatQueryParameterList(productRoles))
.build()
.constructMap();
}

private Map<String, Object> constructUserInstitutionFilterMap(String institutionId) {
return UserInstitutionFilter
.builder()
.institutionId(institutionId)
.build()
.constructMap();
}

private Map<String, Object> constructOnboardedProductFilterMap(String productId) {
return OnboardedProductFilter.builder()
.productId(productId)
.build()
.constructMap();
}

private Map<String, Object> retrieveMapForFilter(Map<String, Object> ... maps) {
Map<String, Object> map = new HashMap<>();
Arrays.stream(maps).forEach(map::putAll);
return map;
}

private boolean checkIfNotFoundException(Throwable throwable) {
if(throwable instanceof WebClientApplicationException wex) {
return wex.getResponse().getStatus() == HttpStatus.SC_NOT_FOUND;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
package it.pagopa.selfcare.user.util;

import io.smallrye.mutiny.Uni;
import it.pagopa.selfcare.onboarding.common.PartyRole;
import it.pagopa.selfcare.product.entity.ProductRole;
import it.pagopa.selfcare.product.service.ProductService;
import it.pagopa.selfcare.user.exception.InvalidRequestException;
import jakarta.enterprise.context.ApplicationScoped;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.gradle.internal.impldep.org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.reactive.client.api.WebClientApplicationException;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import java.util.Arrays;
import java.util.HashMap;
Expand All @@ -13,10 +25,30 @@
@Slf4j
public class UserUtils {

private final ProductService productService;

@SafeVarargs
public final Map<String, Object> retrieveMapForFilter(Map<String, Object>... maps) {
Map<String, Object> map = new HashMap<>();
Arrays.stream(maps).forEach(map::putAll);
return map;
}

public void checkProductRole(String productId, PartyRole role, String productRole) {
if(StringUtils.isNotBlank(productRole) && StringUtils.isNotBlank(productId)) {
try {
productService.validateProductRole(productId, productRole, role);
}catch (IllegalArgumentException e){
throw new InvalidRequestException(e.getMessage());
}
}
}

public static boolean checkIfNotFoundException(Throwable throwable) {
if(throwable instanceof WebClientApplicationException wex) {
return wex.getResponse().getStatus() == HttpStatus.SC_NOT_FOUND;
}

return false;
}
}
2 changes: 1 addition & 1 deletion apps/user-ms/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ quarkus.rest-client."org.openapi.quarkus.user_registry_json.api.UserApi".url=${U
## AZURE STORAGE ##

## Jacoco
quarkus.jacoco.includes=it/pagopa/selfcare/user/controller/*,it/pagopa/selfcare/user/service/**,it/pagopa/selfcare/user/repository/**
quarkus.jacoco.includes=it/pagopa/selfcare/user/controller/*,it/pagopa/selfcare/user/service/**,it/pagopa/selfcare/user/util/**
quarkus.jacoco.data-file=target/jacoco.exec
Loading

0 comments on commit 343c8a6

Please sign in to comment.