diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java index 8ab93a2a4edaa5a1d905fdfa5fcad9d200df6316..941d786e1b7f5896114707c8ef9b737648d5d9c9 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java @@ -24,6 +24,8 @@ public interface AccountRepositoryAdditional { Profile updateProfile(UpdateProfileRequest request); + boolean deleteAccount(UUID accountId); + boolean foundExistingNotOwnUsername(UUID accountId, String username); PendingAccountByAuthenticationProvider findSameAuthenticationProviderInPendingAccount(AuthenticationProvider authProvider); diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index c1b1f0efa6e54b59fdfcfe6d677b2fce17e7b9df..17e055652127a8076f0cd26803229f59ae444a4c 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -25,6 +25,7 @@ import biz.nynja.account.grpc.UpdateProfileRequest; import biz.nynja.account.models.Account; import biz.nynja.account.models.AccountByAuthenticationProvider; import biz.nynja.account.models.AccountByCommunicationProvider; +import biz.nynja.account.models.AccountByProfileId; import biz.nynja.account.models.AccountByUsername; import biz.nynja.account.models.AuthenticationProvider; import biz.nynja.account.models.PendingAccount; @@ -55,6 +56,9 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio @Autowired AccountByAuthenticationProviderRepository accountByAuthenticationProviderRepository; + @Autowired + AccountByProfileIdRepository accountByProfileIdRepository; + @Autowired ProfileByAuthenticationProviderRepository profileByAuthenticationProviderRepository; @@ -198,7 +202,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio updateProfileData(batchOperations, request, existingProfile, timeUpdated); insertNewAuthenticationProvidersToProfile(batchOperations, existingProfile.getProfileId(), newAuthenticationProvidersSet); - deleteOldAuthenticationProvidersFromProfile(batchOperations, existingProfile.getProfileId(), + deleteAuthenticationProvidersFromProfile(batchOperations, existingProfile.getProfileId(), oldAuthenticationProvidersSet); wr = batchOperations.execute(); } catch (IllegalArgumentException | IllegalStateException e) { @@ -261,7 +265,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio try { updateAccountData(batchOperations, request, existingAccount, timeUpdated); insertNewCommunicationProvidersToAccount(batchOperations, request, newCommunicationProvidersSet); - deleteOldCommunicationProvidersFromAccount(batchOperations, existingAccount, oldCommunicationProvidersSet); + deleteCommunicationProvidersFromAccount(batchOperations, existingAccount, oldCommunicationProvidersSet); updateSameCommunicationProvidersFromAccount(batchOperations, request, sameCommunicationProvidersSet); wr = batchOperations.execute(); } catch (IllegalArgumentException | IllegalStateException e) { @@ -280,6 +284,98 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio return null; } + public boolean deleteAccount(UUID accountId) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Account existingAccount = accountRepository.findByAccountId(accountId); + if (!doExistAccountAndProfileToDelete(accountId)) { + return false; + } + Profile existingProfile = profileRepository.findByProfileId(existingAccount.getProfileId()); + List existingAccountsForProfile = accountByProfileIdRepository + .findAllByProfileId(existingAccount.getProfileId()); + if (existingAccountsForProfile == null) { + logger.error("Error deleting account. Existing accounts for the given profile id {} were not found.", + existingAccount.getProfileId()); + return false; + } + boolean alsoDeleteProfile = false; + // check for last account in the profile + if (existingAccountsForProfile.size() == 1) { + alsoDeleteProfile = true; + } + + Set existingCommunicationProvidersSet = new HashSet(); + if (existingAccount.getCommunicationProviders() != null) { + existingCommunicationProvidersSet = new HashSet( + existingAccount.getCommunicationProviders()); + } + WriteResult wr = null; + try { + deleteAccountData(batchOperations, existingAccount); + deleteCommunicationProvidersFromAccount(batchOperations, existingAccount, + existingCommunicationProvidersSet); + deleteProfileByAuthenticationProvider(batchOperations, existingAccount.getProfileId(), + AuthenticationProvider.createAuthenticationProviderFromStrings( + existingAccount.getAuthenticationProviderType(), + existingAccount.getAuthenticationProvider())); + if (alsoDeleteProfile) { + if (existingProfile.getAuthenticationProviders() != null) { + deleteAuthenticationProvidersFromProfile(batchOperations, existingProfile.getProfileId(), + existingProfile.getAuthenticationProviders()); + } + deleteProfileData(batchOperations, existingProfile); + } else { + if (!removeCreationProvider(batchOperations, existingAccount, existingProfile)) { + return false; + } + } + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.error("Exception while deleting account: {}.", e.getMessage()); + return false; + } + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + + /** + * The method removes the deleted account's creation provider from the list of auth providers of the profile (if not + * already manually removed by the user). + * + * @param batchOperations + * @param existingAccount + * @param existingProfile + * @return true or false according to the result of removing the account's creation provider + */ + private boolean removeCreationProvider(CassandraBatchOperations batchOperations, Account existingAccount, + Profile existingProfile) { + // update authentication providers of the profile by removing the one of the deleted account (if not + // already manually removed by the user) + Long timeUpdated = new Date().getTime(); + if (existingProfile.getAuthenticationProviders() != null) { + Set existingAuthenticationProvidersSet = new HashSet( + existingProfile.getAuthenticationProviders()); + boolean removedAuthProvider = existingAuthenticationProvidersSet.remove(AuthenticationProvider + .createAuthenticationProviderFromStrings(existingAccount.getAuthenticationProviderType(), + existingAccount.getAuthenticationProvider())); + if (removedAuthProvider) { + // at least one authentication provider should exist in the profile + if (existingAuthenticationProvidersSet.size() > 0) { + updateAuthProvidersInProfileWhenDeletingAccount(batchOperations, existingProfile, + existingAuthenticationProvidersSet, timeUpdated); + } else { + logger.error( + "Error deleting account. At least one authentication provider should exist in profile: {}.", + existingAccount.getProfileId()); + return false; + } + } + } + return true; + } + private void updateAccountData(CassandraBatchOperations batchOps, UpdateAccountRequest request, Account existingAccount, Long lastUpdateTimestamp) { Account updatedAccount = existingAccount; @@ -328,6 +424,40 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio batchOps.update(updatedProfile); } + private void updateAuthProvidersInProfileWhenDeletingAccount(CassandraBatchOperations batchOps, Profile existingProfile, Set authProvidersToUpdate, Long lastUpdateTimestamp) { + Profile updatedProfile = existingProfile; + if (authProvidersToUpdate != null) { + updatedProfile.setAuthenticationProviders(authProvidersToUpdate); + updatedProfile.setLastUpdateTimestamp(lastUpdateTimestamp); + batchOps.update(updatedProfile); + } + } + + private boolean doExistAccountAndProfileToDelete(UUID accountId) { + Account existingAccount = accountRepository.findByAccountId(accountId); + if (existingAccount == null) { + logger.error("Error deleting account. Existing account with the provided id {} was not found.", accountId); + return false; + } + Profile existingProfile = profileRepository.findByProfileId(existingAccount.getProfileId()); + if (existingProfile == null) { + logger.error("Error deleting account. Corresponding profile with the provided id {} was not found.", + existingAccount.getProfileId()); + return false; + } + return true; + } + + private void deleteAccountData(CassandraBatchOperations batchOps, Account existingAccountToDelete) { + Account accountToDelete = existingAccountToDelete; + batchOps.delete(accountToDelete); + } + + private void deleteProfileData(CassandraBatchOperations batchOps, Profile existingProfileToDelete) { + Profile profileToDelete = existingProfileToDelete; + batchOps.delete(profileToDelete); + } + private void insertNewCommunicationProvidersToAccount(CassandraBatchOperations batchOps, UpdateAccountRequest request, Set newCommunicationProvidersSet) { for (AuthenticationProvider commProvider : newCommunicationProvidersSet) { @@ -335,10 +465,10 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio } } - private void deleteOldCommunicationProvidersFromAccount(CassandraBatchOperations batchOps, Account existingAccount, - Set oldCommunicationProvidersSet) { - for (AuthenticationProvider commProvider : oldCommunicationProvidersSet) { - deleteOldAccountByCommunicationProvider(batchOps, existingAccount, commProvider); + private void deleteCommunicationProvidersFromAccount(CassandraBatchOperations batchOps, Account existingAccount, + Set communicationProvidersSetToDelete) { + for (AuthenticationProvider commProvider : communicationProvidersSetToDelete) { + deleteAccountByCommunicationProvider(batchOps, existingAccount, commProvider); } } @@ -365,20 +495,20 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio batchOps.insert(newProfileByAuthenticationProvider); } - private void deleteOldAuthenticationProvidersFromProfile(CassandraBatchOperations batchOps, UUID profileId, - Set oldAuthenticationProvidersSet) { - for (AuthenticationProvider authProvider : oldAuthenticationProvidersSet) { - deleteOldProfileByAuthenticationProvider(batchOps, profileId, authProvider); + private void deleteAuthenticationProvidersFromProfile(CassandraBatchOperations batchOps, UUID profileId, + Set authenticationProvidersSetToDelete) { + for (AuthenticationProvider authProvider : authenticationProvidersSetToDelete) { + deleteProfileByAuthenticationProvider(batchOps, profileId, authProvider); } } - private void deleteOldProfileByAuthenticationProvider(CassandraBatchOperations batchOps, UUID profileId, + private void deleteProfileByAuthenticationProvider(CassandraBatchOperations batchOps, UUID profileId, AuthenticationProvider authProvider) { - ProfileByAuthenticationProvider oldProfileByAuthenticationProvider = new ProfileByAuthenticationProvider(); - oldProfileByAuthenticationProvider.setAuthenticationProvider(authProvider.getValue()); - oldProfileByAuthenticationProvider.setAuthenticationProviderType(authProvider.getType()); - oldProfileByAuthenticationProvider.setProfileId(profileId); - batchOps.delete(oldProfileByAuthenticationProvider); + ProfileByAuthenticationProvider profileByAuthenticationProviderToDelete = new ProfileByAuthenticationProvider(); + profileByAuthenticationProviderToDelete.setAuthenticationProvider(authProvider.getValue()); + profileByAuthenticationProviderToDelete.setAuthenticationProviderType(authProvider.getType()); + profileByAuthenticationProviderToDelete.setProfileId(profileId); + batchOps.delete(profileByAuthenticationProviderToDelete); } private void insertNewAccountByCommunicationProvider(CassandraBatchOperations batchOps, @@ -393,16 +523,16 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio batchOps.insert(newAccountByCommunicationProvider); } - private void deleteOldAccountByCommunicationProvider(CassandraBatchOperations batchOps, Account existingAccount, + private void deleteAccountByCommunicationProvider(CassandraBatchOperations batchOps, Account existingAccount, AuthenticationProvider authProvider) { - AccountByCommunicationProvider oldAccountByCommunicationProvider = new AccountByCommunicationProvider(); - oldAccountByCommunicationProvider.setCommunicationProvider(authProvider.getValue()); - oldAccountByCommunicationProvider.setCommunicationProviderType(authProvider.getType()); - oldAccountByCommunicationProvider.setAccountId(existingAccount.getAccountId()); - oldAccountByCommunicationProvider.setAccountName(existingAccount.getAccountName()); - oldAccountByCommunicationProvider.setFirstName(existingAccount.getFirstName()); - oldAccountByCommunicationProvider.setAvatar(existingAccount.getAvatar()); - batchOps.delete(oldAccountByCommunicationProvider); + AccountByCommunicationProvider accountByCommunicationProviderToDelete = new AccountByCommunicationProvider(); + accountByCommunicationProviderToDelete.setCommunicationProvider(authProvider.getValue()); + accountByCommunicationProviderToDelete.setCommunicationProviderType(authProvider.getType()); + accountByCommunicationProviderToDelete.setAccountId(existingAccount.getAccountId()); + accountByCommunicationProviderToDelete.setAccountName(existingAccount.getAccountName()); + accountByCommunicationProviderToDelete.setFirstName(existingAccount.getFirstName()); + accountByCommunicationProviderToDelete.setAvatar(existingAccount.getAvatar()); + batchOps.delete(accountByCommunicationProviderToDelete); } private void updateSameAccountByCommunicationProvider(CassandraBatchOperations batchOps, diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index 7aede542969b47fd3f30ada47644eb7a251a29ba..c3cb47e8e463dba2443e6b063486dda214a0cd16 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -26,6 +26,8 @@ import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; import biz.nynja.account.grpc.CreateAccountRequest; import biz.nynja.account.grpc.CreatePendingAccountRequest; import biz.nynja.account.grpc.CreatePendingAccountResponse; +import biz.nynja.account.grpc.DeleteAccountRequest; +import biz.nynja.account.grpc.StatusResponse; import biz.nynja.account.grpc.ErrorResponse; import biz.nynja.account.grpc.ErrorResponse.Cause; import biz.nynja.account.models.Account; @@ -413,4 +415,29 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas logger.info("Account updated successfully."); return; } + + @Override + public void deleteAccount(DeleteAccountRequest request, StreamObserver responseObserver) { + logger.debug("Deleting account...: {}", request); + + if ((request.getAccountId() == null) || (request.getAccountId().isEmpty())) { + responseObserver.onNext(StatusResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.MISSING_ACCOUNT_ID)).build()); + responseObserver.onCompleted(); + return; + } + + boolean wasAccountDeleted = accountRepositoryAdditional.deleteAccount(UUID.fromString(request.getAccountId())); + if (wasAccountDeleted) { + responseObserver.onNext(StatusResponse.newBuilder() + .setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + + responseObserver.onNext(StatusResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.ERROR_DELETING_ACCOUNT)).build()); + responseObserver.onCompleted(); + return; + } } \ No newline at end of file