Skip to content

Commit

Permalink
[SELC-5265] fix: Enhancement of UserInfo Record Update Logic (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
manuraf authored Sep 3, 2024
1 parent 8dd7bd2 commit 1a22b51
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.bson.Document;

import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static java.util.function.Predicate.not;

Expand All @@ -28,13 +32,16 @@ public class UserInstitutionRepository {
private final UserMapper userMapper;

public Uni<Void> updateUser(UserInstitution userInstitution) {
Optional<OnboardedProductState> optState = retrieveStatusForGivenInstitution(userInstitution.getProducts());
Optional<OnboardedProductState> optStateToSet = retrieveStatusForGivenInstitution(userInstitution.getProducts());
return UserInfo.findByIdOptional(userInstitution.getUserId())
.onItem().transformToUni(opt -> opt.map(entityBase -> {
if (optState.isPresent() && VALID_PRODUCT_STATE.contains(optState.get())) {
Optional<PartyRole> optRole = retrieveRoleForGivenInstitution(userInstitution.getProducts());
return optRole.isPresent()
? updateOrCreateNewUserInfo(opt.get(), userInstitution, optRole.get(), optState.get())
.onItem().transformToUni(opt -> opt
.map(entityBase -> {
// Check if user has a record with valid state and role to enter inside dashboard,
// in case we must add or update institution reference record
if (optStateToSet.isPresent() && VALID_PRODUCT_STATE.contains(optStateToSet.get())) {
Optional<PartyRole> optRoleToSet = retrieveRoleForGivenInstitution(userInstitution.getProducts());
return optRoleToSet.isPresent()
? addOrUpdateUserInstitutionRole(opt.get(), userInstitution, optRoleToSet.get(), optStateToSet.get())
: Uni.createFrom().voidItem();
} else {
return deleteInstitutionOrAllUserInfo(opt.get(), userInstitution);
Expand Down Expand Up @@ -67,9 +74,12 @@ private Uni<Void> createNewUserInfo(UserInstitution userInstitution) {
userInfo.setUserId(userInstitution.getUserId());
userInfo.setInstitutions(List.of(institutionRole));

return UserInfo.persistOrUpdate(userInfo)
// flow of new user must persist userInfo, if already exists it must be failed
return UserInfo.persist(userInfo)
.invoke(() -> log.info(String.format("createNewUserInfo for userId %s and institution %s",
userInstitution.getUserId(),userInstitution.getInstitutionId())))
.onFailure().invoke(() -> log.error(String.format("createNewUserInfo failed for userId %s and institution %s",
userInstitution.getUserId(),userInstitution.getInstitutionId())))
.replaceWith(Uni.createFrom().voidItem());
}

Expand All @@ -94,38 +104,60 @@ private Uni<Void> deleteInstitutionOrAllUserInfo(ReactivePanacheMongoEntityBase
});
}

private Uni<Void> updateOrCreateNewUserInfo(ReactivePanacheMongoEntityBase entityBase, UserInstitution userInstitution, PartyRole role, OnboardedProductState state) {
private Uni<Void> addOrUpdateUserInstitutionRole(ReactivePanacheMongoEntityBase entityBase, UserInstitution userInstitution, PartyRole role, OnboardedProductState state) {
return Uni.createFrom().item((UserInfo) entityBase)
.map(userInfo -> replaceOrAddInstitution(userInfo, userInstitution, role, state))
.flatMap(userInfo -> UserInfo.persistOrUpdate(userInfo));
.onItem().transformToUni(userInfo -> {

// If the institution record already exists we must update the record using $set,
// otherwise we must add a new record inside array using $addToSet
Document institutionRoleAsDocument = getUserInstitutionRoleAsDocument(userInstitution, role, state);

return userInfo.getInstitutions().stream()
.filter(userInstitutionRole -> userInstitution.getInstitutionId().equalsIgnoreCase(userInstitutionRole.getInstitutionId()))
.findAny()
.map(userInstitutionRole -> updateUserInstitutionRole(userInstitution.getUserId(), userInstitution.getInstitutionId(), institutionRoleAsDocument))
.orElse(addUserInstitutionRole(userInstitution.getUserId(), institutionRoleAsDocument));

})
.onItem().transformToUni(reactiveUpdate -> {
if(reactiveUpdate != 1L) {
log.error(String.format("addOrUpdateUserInstitutionRole failed for userId %s and institution %s",
userInstitution.getUserId(),userInstitution.getInstitutionId()));
}
return Uni.createFrom().voidItem();
});
}

private UserInfo replaceOrAddInstitution(UserInfo userInfo, UserInstitution userInstitution, PartyRole role, OnboardedProductState state) {
if (CollectionUtils.isEmpty(userInfo.getInstitutions())) {
userInfo.setInstitutions(new ArrayList<>());
}
private Uni<Long> updateUserInstitutionRole(String userId, String institutionId, Document institution){
Document updateAddToSet = new Document("$set", new Document("institutions.$", institution));
Document filter = new Document("_id", userId)
.append("institutions.institutionId", institutionId);

return UserInfo
.update(updateAddToSet)
.where(filter);
}

private Uni<Long> addUserInstitutionRole(String userId, Document institution){
Document updateAddToSet = new Document("$addToSet", new Document("institutions", institution));

return UserInfo
.update(updateAddToSet)
.where("_id", userId);
}

userInfo.getInstitutions().stream()
.filter(userInstitutionRole -> userInstitution.getInstitutionId().equalsIgnoreCase(userInstitutionRole.getInstitutionId()))
.findFirst()
.ifPresentOrElse(userInstitutionRole -> {
userInstitutionRole.setRole(role);
userInstitutionRole.setStatus(state);
userInstitutionRole.setInstitutionName(userInstitution.getInstitutionDescription());
userInstitutionRole.setInstitutionRootName(userInstitution.getInstitutionRootName());
userInstitutionRole.setUserMailUuid(userInstitution.getUserMailUuid());
log.info(String.format("replaceOrAddInstitution execution setting role for userId: %s, institutionId: %s, role: %s",
userInstitution.getUserId(), userInstitution.getInstitutionId(), role.name()));
},
() -> {
List<UserInstitutionRole> roleList = new ArrayList<>(userInfo.getInstitutions());
roleList.add(userMapper.toUserInstitutionRole(userInstitution, role, state));
userInfo.setInstitutions(roleList);
log.info(String.format("replaceOrAddInstitution execution adding role for userId: %s, institutionId: %s, role: %s",
userInstitution.getUserId(), userInstitution.getInstitutionId(), role.name()));
});

return userInfo;
private static Document getUserInstitutionRoleAsDocument(UserInstitution userInstitution, PartyRole role, OnboardedProductState state) {
Document institution = new Document("institutionId", userInstitution.getInstitutionId())
.append("role", role)
.append("status", state);

Optional.ofNullable(userInstitution.getInstitutionDescription())
.ifPresent(value -> institution.append("institutionName", value));
Optional.ofNullable(userInstitution.getInstitutionRootName())
.ifPresent(value -> institution.append("institutionRootName", value));
Optional.ofNullable(userInstitution.getUserMailUuid())
.ifPresent(value -> institution.append("userMailUuid", value));
return institution;
}

private Optional<PartyRole> retrieveRoleForGivenInstitution(List<OnboardedProduct> products) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package it.pagopa.selfcare.user.event.repository;

import io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate;
import io.quarkus.panache.mock.PanacheMock;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
Expand All @@ -15,6 +16,7 @@
import it.pagopa.selfcare.user.model.OnboardedProduct;
import it.pagopa.selfcare.user.model.constants.OnboardedProductState;
import jakarta.inject.Inject;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
Expand All @@ -25,8 +27,9 @@
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@QuarkusTest
@QuarkusTestResource(MongoTestResource.class)
Expand Down Expand Up @@ -71,15 +74,19 @@ void mockRetrieveUserInfoFounded(UniAsserter asserter) {
void initOrderStreamWithFoundedUserIdAndInstitutionId(UniAsserter asserter){
UserInstitution userInstitution = constructUserInstitution("productId");
mockRetrieveUserInfoFounded(asserter);
mockPersistUserInfo(asserter);

PanacheMock.mock(UserInfo.class);
ReactivePanacheUpdate updateMock = mock(ReactivePanacheUpdate.class);
when(UserInfo.update(any(Document.class))).thenReturn(updateMock);
when(updateMock.where(any(Document.class))).thenReturn(Uni.createFrom().item(1L));

when(UserInfo.findByIdOptional(any()))
.thenReturn(Uni.createFrom().item(Optional.of(retrieveUserInfo())));
userInstitutionRepository.updateUser(userInstitution)
.subscribe().withSubscriber(UniAssertSubscriber.create()).assertCompleted();

asserter.execute(() -> {
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persistOrUpdate(Mockito.<UserInfo> any(), any());
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).update(any(Document.class));
});
}

Expand All @@ -88,15 +95,19 @@ void initOrderStreamWithFoundedUserIdAndInstitutionId(UniAsserter asserter){
void initOrderStreamWithFoundedUserId(UniAsserter asserter){
UserInstitution userInstitution = constructUserInstitution("productId");
userInstitution.setInstitutionId("institutionId2");

PanacheMock.mock(UserInfo.class);
when(UserInfo.findByIdOptional(any()))
.thenReturn(Uni.createFrom().item(Optional.of(retrieveUserInfo())));
mockPersistUserInfo(asserter);
ReactivePanacheUpdate updateMock = mock(ReactivePanacheUpdate.class);
when(UserInfo.update(any(Document.class))).thenReturn(updateMock);
when(updateMock.where(any(Document.class))).thenReturn(Uni.createFrom().item(1L));

userInstitutionRepository.updateUser(userInstitution)
.subscribe().withSubscriber(UniAssertSubscriber.create()).assertCompleted();

asserter.execute(() -> {
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persistOrUpdate(Mockito.<UserInfo> any(), any());
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).update(any(Document.class));
});
}

Expand All @@ -105,17 +116,23 @@ void initOrderStreamWithFoundedUserId(UniAsserter asserter){
void initOrderStreamWithFoundedUserIdAndInstitutionEmpty(UniAsserter asserter){
UserInstitution userInstitution = constructUserInstitution("productId");
userInstitution.setInstitutionId("institutionId2");

PanacheMock.mock(UserInfo.class);
UserInfo userInfo = new UserInfo();
userInfo.setInstitutions(List.of());
when(UserInfo.findByIdOptional(any()))
.thenReturn(Uni.createFrom().item(Optional.of(retrieveUserInfo())));
mockPersistUserInfo(asserter);
ReactivePanacheUpdate updateMock = mock(ReactivePanacheUpdate.class);
when(UserInfo.update(any(Document.class))).thenReturn(updateMock);
when(updateMock.where(any(Document.class))).thenReturn(Uni.createFrom().item(1L));


userInstitutionRepository.updateUser(userInstitution)
.subscribe().withSubscriber(UniAssertSubscriber.create()).assertCompleted();

asserter.execute(() -> {
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persistOrUpdate(Mockito.<UserInfo> any(), any());
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).update(any(Document.class));
verify(updateMock, times(1)).where(any(String.class),any(Object.class));
});

}
Expand All @@ -132,7 +149,7 @@ void initOrderStreamWithNotFoundUserId(UniAsserter asserter){

asserter.execute(() -> {
ArgumentCaptor<UserInfo> argumentCaptor = ArgumentCaptor.forClass(UserInfo.class);
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persistOrUpdate(argumentCaptor.capture(), any());
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persist(argumentCaptor.capture(), any());
assertEquals(1, argumentCaptor.getValue().getInstitutions().size());
});
}
Expand All @@ -154,7 +171,7 @@ void initOrderStreamWithNotFoundUserIdAndMoreProduct(UniAsserter asserter){

asserter.execute(() -> {
ArgumentCaptor<UserInfo> argumentCaptor = ArgumentCaptor.forClass(UserInfo.class);
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persistOrUpdate(argumentCaptor.capture(), any());
PanacheMock.verify(UserInfo.class, Mockito.atLeastOnce()).persist(argumentCaptor.capture(), any());
assertEquals(1, argumentCaptor.getValue().getInstitutions().size());
assertEquals(PartyRole.MANAGER, argumentCaptor.getValue().getInstitutions().get(0).getRole());
});
Expand Down

0 comments on commit 1a22b51

Please sign in to comment.