diff --git a/src/main/java/biz/nynja/account/StartupScriptsListener.java b/src/main/java/biz/nynja/account/StartupScriptsListener.java index 90c66ea3fcbb7e071eeb6e8a340d396d7bd55def..d008aa14d4ab26c435d29af7ed35ede30434c637 100644 --- a/src/main/java/biz/nynja/account/StartupScriptsListener.java +++ b/src/main/java/biz/nynja/account/StartupScriptsListener.java @@ -48,7 +48,11 @@ public class StartupScriptsListener { + ".accountbyaccountname AS SELECT * FROM account " + "WHERE accountname IS NOT NULL " + "PRIMARY KEY (accountname, accountid);"; + String scriptAccountViewByUsername = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".accountbyusername AS SELECT * FROM account " + "WHERE username IS NOT NULL " + + "PRIMARY KEY (username, accountid);"; + return Arrays.asList(scriptAccountViewByProfileId, scriptAccountViewByAuthProvider, - scriptAccountViewByАccountName); + scriptAccountViewByАccountName, scriptAccountViewByUsername); } } \ No newline at end of file diff --git a/src/main/java/biz/nynja/account/components/Validator.java b/src/main/java/biz/nynja/account/components/Validator.java index d8fdc753639e03bd41f00054c4c3423a4b6d864e..d20ee10a9abb6ae584fb3381e9d245a57ad10685 100644 --- a/src/main/java/biz/nynja/account/components/Validator.java +++ b/src/main/java/biz/nynja/account/components/Validator.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -22,7 +23,9 @@ import org.springframework.stereotype.Component; import biz.nynja.account.grpc.CreateAccountRequest; import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.grpc.UpdateAccountRequest; import biz.nynja.account.models.CountryInfo; +import biz.nynja.account.repositories.AccountRepositoryAdditional; /** * Component which contains all validation methods. @@ -35,6 +38,9 @@ public class Validator { private HashMap countryInfoMap; + @Autowired + private AccountRepositoryAdditional accountRepositoryAdditional; + @PostConstruct public void loadPhonesBook() { @@ -197,4 +203,28 @@ public class Validator { // return null; // } + public Cause validateUpdateAccountRequest(UpdateAccountRequest request) { + + if (request.getUsername() != null && !request.getUsername().trim().isEmpty() + && !isUsernameValid(request.getUsername())) { + return Cause.USERNAME_INVALID; + } else if (request.getUsername() != null && !request.getUsername().trim().isEmpty() + && accountRepositoryAdditional.foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), request.getUsername())) { + return Cause.USERNAME_ALREADY_USED; + } + + if (request.getFirstName() != null && request.getFirstName().trim().isEmpty()) { + return Cause.MISSING_FIRST_NAME; + } else if (!isFirstNameValid(request.getFirstName())) { + return Cause.INVALID_FIRST_NAME; + } + + if (request.getLastName() != null && !request.getLastName().trim().isEmpty() + && !isLastNameValid(request.getLastName())) { + return Cause.INVALID_LAST_NAME; + } + + return null; + } + } diff --git a/src/main/java/biz/nynja/account/models/AccountByUsername.java b/src/main/java/biz/nynja/account/models/AccountByUsername.java new file mode 100644 index 0000000000000000000000000000000000000000..c25bf945cbc08640eb292e97188cda5c8cb6cb7b --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AccountByUsername.java @@ -0,0 +1,273 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.util.Set; +import java.util.UUID; + +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.AccountDetails.Builder; + +public class AccountByUsername { + + private UUID profileId; + private UUID accountId; + private String accountMark; + private String authenticationProvider; + private String authenticationProviderType; + private String firstName; + private String lastName; + private ByteBuffer avatar; + private String accountName; + private String username; + private String accountStatus; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + private Set communicationProviders; + private String qrCode; + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public String getAccountMark() { + return accountMark; + } + + public void setAccountMark(String accountMark) { + this.accountMark = accountMark; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAccountStatus() { + return accountStatus; + } + + public void setAccountStatus(String accountStatus) { + this.accountStatus = accountStatus; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public Set getCommunicationProviders() { + return communicationProviders; + } + + public void setCommunicationProviders(Set communicationProviders) { + this.communicationProviders = communicationProviders; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountMark == null) ? 0 : accountMark.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((accountStatus == null) ? 0 : accountStatus.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((communicationProviders == null) ? 0 : communicationProviders.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + result = prime * result + ((qrCode == null) ? 0 : qrCode.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountByUsername other = (AccountByUsername) obj; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountMark == null) { + if (other.accountMark != null) + return false; + } else if (!accountMark.equals(other.accountMark)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (accountStatus == null) { + if (other.accountStatus != null) + return false; + } else if (!accountStatus.equals(other.accountStatus)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (communicationProviders == null) { + if (other.communicationProviders != null) + return false; + } else if (!communicationProviders.equals(other.communicationProviders)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + if (qrCode == null) { + if (other.qrCode != null) + return false; + } else if (!qrCode.equals(other.qrCode)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("Account [accountId=").append(accountId).append(", profileId=").append(profileId) + .append(", accountMark=").append(accountMark).append(", authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", firstName=").append(firstName).append(", lastName=") + .append(lastName).append(", avatar=").append(avatar).append(", accountName=").append(accountName) + .append(", username=").append(username).append(", accountStatus=").append(accountStatus) + .append(", creationTimestamp=").append(creationTimestamp).append(", lastUpdateTimestamp=") + .append(lastUpdateTimestamp).append(", communicationProviders=").append(communicationProviders) + .append("]").toString(); + } + +} diff --git a/src/main/java/biz/nynja/account/models/Profile.java b/src/main/java/biz/nynja/account/models/Profile.java index d29d961b79650050378d66e95c1b53c7cb53ab53..86071a31b0f2367bb50735e8320d398084355429 100644 --- a/src/main/java/biz/nynja/account/models/Profile.java +++ b/src/main/java/biz/nynja/account/models/Profile.java @@ -9,6 +9,9 @@ import java.util.UUID; import org.springframework.data.cassandra.core.mapping.PrimaryKey; import org.springframework.data.cassandra.core.mapping.Table; +import biz.nynja.account.grpc.ProfileResponse; +import biz.nynja.account.grpc.ProfileResponse.Builder; + @Table public class Profile { @@ -148,4 +151,26 @@ public class Profile { .append(backupAuthenticationProvider).append("]").toString(); } + public ProfileResponse toProto() { + Builder builder = ProfileResponse.newBuilder(); + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getPasscode() != null) { + builder.setPasscode(getPasscode()); + } + if (getDefaultAccount() != null) { + builder.setDefaultAccountId(getDefaultAccount().toString()); + } + if (getBackupAuthenticationProvider() != null) { + builder.setBackupAuthProvider(getBackupAuthenticationProvider().toProto()); + } + if (getAuthenticationProviders() != null) { + for (AuthenticationProvider authenticationProvider : authenticationProviders) { + builder.addAuthProviders(authenticationProvider.toProto()); + } + } + return builder.build(); + } + } diff --git a/src/main/java/biz/nynja/account/repositories/AccountByUsernameRepository.java b/src/main/java/biz/nynja/account/repositories/AccountByUsernameRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..647a9188989fb1c703a09f2302d521e3ac7d7eaf --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountByUsernameRepository.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.AccountByUsername; + +@Repository +public interface AccountByUsernameRepository extends CassandraRepository { + + AccountByUsername findByUsername(String username); + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepository.java b/src/main/java/biz/nynja/account/repositories/AccountRepository.java index 783be8f098ad368dc698e8d6c6fcb91d9de2a327..6dd80547dc3a5fff699856e8c60f269d5b0711f6 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepository.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepository.java @@ -13,6 +13,6 @@ import biz.nynja.account.models.Account; @Repository public interface AccountRepository extends CassandraRepository { - public Account findByAccountId(UUID accountId); + Account findByAccountId(UUID accountId); } diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java index 4bd825266e0aa540c54004342d0d687072c65851..099850881e2166caac0492a7d79f478ba075d2d1 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java @@ -3,17 +3,25 @@ */ package biz.nynja.account.repositories; +import java.util.UUID; + import org.springframework.stereotype.Repository; import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; -import biz.nynja.account.grpc.CreateAccountRequest; -import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; import biz.nynja.account.models.Account; -import biz.nynja.account.models.PendingAccount; +import biz.nynja.account.models.Profile; @Repository public interface AccountRepositoryAdditional { - public Account completePendingAccountCreation(CompletePendingAccountCreationRequest request); + Account completePendingAccountCreation(CompletePendingAccountCreationRequest request); + + Account updateAccount(UpdateAccountRequest request); + + Profile updateProfile(UpdateProfileRequest request); + + boolean foundExistingNotOwnUsername(UUID accountId, String username); } diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index b9f54222ac404eac18ef952f6a4a5cc2380954dd..f6c0761bf7f8c3571d648fa3052b48506fad7b24 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -16,8 +16,13 @@ import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.core.WriteResult; import org.springframework.stereotype.Service; +import biz.nynja.account.grpc.AuthProviderDetails; import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByCommunicationProvider; +import biz.nynja.account.models.AccountByUsername; import biz.nynja.account.models.AuthenticationProvider; import biz.nynja.account.models.PendingAccount; import biz.nynja.account.models.Profile; @@ -27,6 +32,7 @@ import biz.nynja.account.models.ProfileByAuthenticationProvider; public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditional { private static final Logger logger = LoggerFactory.getLogger(AccountRepositoryAdditionalImpl.class); + private static final int COMPLETE_PENDING_ACCOUNT_TIMEOUT = 30 * 60 * 1000; @Autowired private CassandraTemplate cassandraTemplate; @@ -34,10 +40,15 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio @Autowired private AccountRepository accountRepository; + @Autowired + private ProfileRepository profileRepository; + + @Autowired + AccountByUsernameRepository accountByUsernameRepository; + @Autowired private PendingAccountRepository pendingAccountRepository; - @Override public Account completePendingAccountCreation(CompletePendingAccountCreationRequest request) { CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); PendingAccount pendingAccount = pendingAccountRepository @@ -46,36 +57,33 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio logger.info("Existing pending account with the provided id was not found."); logger.debug("Existing pending account with the provided id {} was not found.", request.getAccountId()); return null; - } else { - Long timeCreated = new Date().getTime(); - Long checkMinutes = timeCreated - pendingAccount.getCreationTimestamp(); - if (checkMinutes > 30 * 60 * 1000) { - logger.info("Account creation timeout expired."); - return null; - } else { - WriteResult wr = null; - try { - newAccountInsert(batchOperations, request, pendingAccount, timeCreated); - newProfileInsert(batchOperations, request, pendingAccount, timeCreated); - newProfileByAuthenticationProviderInsert(batchOperations, pendingAccount); - wr = batchOperations.execute(); - } catch (IllegalArgumentException | IllegalStateException e) { - logger.info("Exception while completing pending account creation."); - logger.debug("Exception while completing pending account creation: {} ...", e.getMessage()); - return null; - } - if (wr != null) { - boolean applied = wr.wasApplied(); - if (applied) { - pendingAccountRepository.deleteById(UUID.fromString(request.getAccountId())); - Account createdAccount = accountRepository - .findByAccountId(UUID.fromString(request.getAccountId())); - return createdAccount; - } - } - return null; + } + Long timeCreated = new Date().getTime(); + Long checkMinutes = timeCreated - pendingAccount.getCreationTimestamp(); + if (checkMinutes > COMPLETE_PENDING_ACCOUNT_TIMEOUT) { + logger.info("Account creation timeout expired."); + return null; + } + WriteResult wr = null; + try { + newAccountInsert(batchOperations, request, pendingAccount, timeCreated); + newProfileInsert(batchOperations, request, pendingAccount, timeCreated); + newProfileByAuthenticationProviderInsert(batchOperations, pendingAccount); + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while completing pending account creation."); + logger.debug("Exception while completing pending account creation: {} ...", e.getMessage()); + return null; + } + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + pendingAccountRepository.deleteById(UUID.fromString(request.getAccountId())); + Account createdAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + return createdAccount; } } + return null; } private void newAccountInsert(CassandraBatchOperations batchOps, CompletePendingAccountCreationRequest request, @@ -95,7 +103,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio newAccount.setCreationTimestamp(creationTimestamp); Set communicationProvidersSet = new HashSet(); newAccount.setCommunicationProviders(communicationProvidersSet); - if (request.getCommunicationProvidersList() != null) { + if ((request.getCommunicationProvidersList() != null) && (request.getCommunicationProvidersList().size() > 0)) { for (int i = 0; i < request.getCommunicationProvidersList().size(); i++) { communicationProvidersSet.add(AuthenticationProvider .createAuthenticationProviderFromProto(request.getCommunicationProviders(i))); @@ -128,4 +136,280 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio batchOps.insert(newProfileByAuthenticationProvider); } + public Profile updateProfile(UpdateProfileRequest request) { + + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Profile existingProfile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId())); + if (existingProfile == null) { + logger.info("Existing profile with the provided id was not found."); + logger.debug("Existing profile with the provided id {} was not found.", request.getProfileId()); + return null; + } + Long timeUpdated = new Date().getTime(); + Set existingAuthenticationProvidersSet = new HashSet(); + if (existingProfile.getAuthenticationProviders() != null) { + existingAuthenticationProvidersSet = new HashSet( + existingProfile.getAuthenticationProviders()); + } + Set requestedAuthenticationProvidersSet = new HashSet(); + Set sameAuthenticationProvidersSet = new HashSet(); + Set oldAuthenticationProvidersSet = new HashSet(); + Set newAuthenticationProvidersSet = new HashSet(); + + if (request.getAuthProvidersList() != null) { + for (AuthProviderDetails authProviderDetails : request.getAuthProvidersList()) { + requestedAuthenticationProvidersSet + .add(AuthenticationProvider.createAuthenticationProviderFromProto(authProviderDetails)); + } + } + + try { + sameAuthenticationProvidersSet = new HashSet(requestedAuthenticationProvidersSet); + oldAuthenticationProvidersSet = new HashSet(existingAuthenticationProvidersSet); + newAuthenticationProvidersSet = new HashSet(requestedAuthenticationProvidersSet); + + sameAuthenticationProvidersSet.retainAll(existingAuthenticationProvidersSet); + oldAuthenticationProvidersSet.removeAll(sameAuthenticationProvidersSet); + newAuthenticationProvidersSet.removeAll(sameAuthenticationProvidersSet); + } catch (Exception e) { + logger.info("Exception while setting authentication providers."); + logger.debug("Exception while setting authentication providers: {} ...", e.getMessage()); + return null; + } + + WriteResult wr = null; + try { + updateProfileData(batchOperations, request, existingProfile, timeUpdated); + insertNewAuthenticationProvidersToProfile(batchOperations, existingProfile.getProfileId(), + newAuthenticationProvidersSet); + deleteOldAuthenticationProvidersFromProfile(batchOperations, existingProfile.getProfileId(), + oldAuthenticationProvidersSet); + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while updating profile."); + logger.debug("Exception while updating profile: {} ...", e.getMessage()); + return null; + } + + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + Profile updatedProfile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId())); + return updatedProfile; + } + } + return null; + } + + public Account updateAccount(UpdateAccountRequest request) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Account existingAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + if (existingAccount == null) { + logger.info("Existing account with the provided id was not found."); + logger.debug("Existing account with the provided id {} was not found.", request.getAccountId()); + return null; + } + Long timeUpdated = new Date().getTime(); + Set existingCommunicationProvidersSet = new HashSet(); + if (existingAccount.getCommunicationProviders() != null) { + existingCommunicationProvidersSet = new HashSet( + existingAccount.getCommunicationProviders()); + } + Set requestedCommunicationProvidersSet = new HashSet(); + Set sameCommunicationProvidersSet = new HashSet(); + Set oldCommunicationProvidersSet = new HashSet(); + Set newCommunicationProvidersSet = new HashSet(); + + if (request.getCommunicationProvidersList() != null) { + for (AuthProviderDetails authProviderDetails : request.getCommunicationProvidersList()) { + requestedCommunicationProvidersSet + .add(AuthenticationProvider.createAuthenticationProviderFromProto(authProviderDetails)); + } + } + + try { + sameCommunicationProvidersSet = new HashSet(requestedCommunicationProvidersSet); + oldCommunicationProvidersSet = new HashSet(existingCommunicationProvidersSet); + newCommunicationProvidersSet = new HashSet(requestedCommunicationProvidersSet); + + sameCommunicationProvidersSet.retainAll(existingCommunicationProvidersSet); + oldCommunicationProvidersSet.removeAll(sameCommunicationProvidersSet); + newCommunicationProvidersSet.removeAll(sameCommunicationProvidersSet); + } catch (Exception e) { + logger.info("Exception while setting communication providers."); + logger.debug("Exception while setting communication providers: {} ...", e.getMessage()); + return null; + } + + WriteResult wr = null; + try { + updateAccountData(batchOperations, request, existingAccount, timeUpdated); + insertNewCommunicationProvidersToAccount(batchOperations, request, newCommunicationProvidersSet); + deleteOldCommunicationProvidersFromAccount(batchOperations, existingAccount, oldCommunicationProvidersSet); + updateSameCommunicationProvidersFromAccount(batchOperations, request, sameCommunicationProvidersSet); + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while updating account."); + logger.debug("Exception while updating account: {} ...", e.getMessage()); + return null; + } + + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + Account updatedAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + return updatedAccount; + } + } + return null; + } + + private void updateAccountData(CassandraBatchOperations batchOps, UpdateAccountRequest request, + Account existingAccount, Long lastUpdateTimestamp) { + Account updatedAccount = existingAccount; + updatedAccount.setAvatar(request.getAvatar().asReadOnlyByteBuffer()); + updatedAccount.setAccountMark(request.getAccountMark()); + updatedAccount.setAccountName(request.getAccountName()); + updatedAccount.setFirstName(request.getFirstName()); + updatedAccount.setLastName(request.getLastName()); + updatedAccount.setUsername(request.getUsername()); + updatedAccount.setAccountStatus(request.getAccountStatus()); + Set communicationProvidersSet = new HashSet(); + if (request.getCommunicationProvidersList() != null) { + for (AuthProviderDetails authProviderDetails : request.getCommunicationProvidersList()) { + communicationProvidersSet + .add(AuthenticationProvider.createAuthenticationProviderFromProto(authProviderDetails)); + } + updatedAccount.setCommunicationProviders(communicationProvidersSet); + } + updatedAccount.setLastUpdateTimestamp(lastUpdateTimestamp); + batchOps.update(updatedAccount); + } + + private void updateProfileData(CassandraBatchOperations batchOps, UpdateProfileRequest request, + Profile existingProfile, Long lastUpdateTimestamp) { + Profile updatedProfile = existingProfile; + Set authProvidersSet = new HashSet(); + if (request.getAuthProvidersList() != null) { + for (AuthProviderDetails authProviderDetails : request.getAuthProvidersList()) { + authProvidersSet.add(AuthenticationProvider.createAuthenticationProviderFromProto(authProviderDetails)); + } + updatedProfile.setAuthenticationProviders(authProvidersSet); + } + if (!request.getBackupAuthProvider().getAuthenticationProvider().trim().isEmpty()) { + updatedProfile.setBackupAuthenticationProvider( + AuthenticationProvider.createAuthenticationProviderFromProto(request.getBackupAuthProvider())); + } else { + updatedProfile.setBackupAuthenticationProvider(null); + } + updatedProfile.setPasscode(request.getPasscode()); + if (!request.getDefaultAccountId().trim().isEmpty()) { + updatedProfile.setDefaultAccount(UUID.fromString(request.getDefaultAccountId())); + } else { + updatedProfile.setDefaultAccount(null); + } + updatedProfile.setLastUpdateTimestamp(lastUpdateTimestamp); + batchOps.update(updatedProfile); + } + + private void insertNewCommunicationProvidersToAccount(CassandraBatchOperations batchOps, + UpdateAccountRequest request, Set newCommunicationProvidersSet) { + for (AuthenticationProvider commProvider : newCommunicationProvidersSet) { + insertNewAccountByCommunicationProvider(batchOps, request, commProvider); + } + } + + private void deleteOldCommunicationProvidersFromAccount(CassandraBatchOperations batchOps, Account existingAccount, + Set oldCommunicationProvidersSet) { + for (AuthenticationProvider commProvider : oldCommunicationProvidersSet) { + deleteOldAccountByCommunicationProvider(batchOps, existingAccount, commProvider); + } + } + + private void updateSameCommunicationProvidersFromAccount(CassandraBatchOperations batchOps, + UpdateAccountRequest request, Set sameCommunicationProvidersSet) { + for (AuthenticationProvider commProvider : sameCommunicationProvidersSet) { + updateSameAccountByCommunicationProvider(batchOps, request, commProvider); + } + } + + private void insertNewAuthenticationProvidersToProfile(CassandraBatchOperations batchOps, UUID profileId, + Set newAuthenticationProvidersSet) { + for (AuthenticationProvider authProvider : newAuthenticationProvidersSet) { + insertNewProfileByAuthenticationProvider(batchOps, profileId, authProvider); + } + } + + private void insertNewProfileByAuthenticationProvider(CassandraBatchOperations batchOps, UUID profileId, + AuthenticationProvider authProvider) { + ProfileByAuthenticationProvider newProfileByAuthenticationProvider = new ProfileByAuthenticationProvider(); + newProfileByAuthenticationProvider.setAuthenticationProvider(authProvider.getValue()); + newProfileByAuthenticationProvider.setAuthenticationProviderType(authProvider.getType()); + newProfileByAuthenticationProvider.setProfileId(profileId); + batchOps.insert(newProfileByAuthenticationProvider); + } + + private void deleteOldAuthenticationProvidersFromProfile(CassandraBatchOperations batchOps, UUID profileId, + Set oldAuthenticationProvidersSet) { + for (AuthenticationProvider authProvider : oldAuthenticationProvidersSet) { + deleteOldProfileByAuthenticationProvider(batchOps, profileId, authProvider); + } + } + + private void deleteOldProfileByAuthenticationProvider(CassandraBatchOperations batchOps, UUID profileId, + AuthenticationProvider authProvider) { + ProfileByAuthenticationProvider oldProfileByAuthenticationProvider = new ProfileByAuthenticationProvider(); + oldProfileByAuthenticationProvider.setAuthenticationProvider(authProvider.getValue()); + oldProfileByAuthenticationProvider.setAuthenticationProviderType(authProvider.getType()); + oldProfileByAuthenticationProvider.setProfileId(profileId); + batchOps.delete(oldProfileByAuthenticationProvider); + } + + private void insertNewAccountByCommunicationProvider(CassandraBatchOperations batchOps, + UpdateAccountRequest request, AuthenticationProvider authProvider) { + AccountByCommunicationProvider newAccountByCommunicationProvider = new AccountByCommunicationProvider(); + newAccountByCommunicationProvider.setCommunicationProvider(authProvider.getValue()); + newAccountByCommunicationProvider.setCommunicationProviderType(authProvider.getType()); + newAccountByCommunicationProvider.setAccountId(UUID.fromString(request.getAccountId())); + newAccountByCommunicationProvider.setAccountName(request.getAccountName()); + newAccountByCommunicationProvider.setFirstName(request.getFirstName()); + newAccountByCommunicationProvider.setAvatar(request.getAvatar().asReadOnlyByteBuffer()); + batchOps.insert(newAccountByCommunicationProvider); + } + + private void deleteOldAccountByCommunicationProvider(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); + } + + private void updateSameAccountByCommunicationProvider(CassandraBatchOperations batchOps, + UpdateAccountRequest request, AuthenticationProvider authProvider) { + AccountByCommunicationProvider oldAccountByCommunicationProvider = new AccountByCommunicationProvider(); + oldAccountByCommunicationProvider.setCommunicationProvider(authProvider.getValue()); + oldAccountByCommunicationProvider.setCommunicationProviderType(authProvider.getType()); + oldAccountByCommunicationProvider.setAccountId(UUID.fromString(request.getAccountId())); + oldAccountByCommunicationProvider.setAccountName(request.getAccountName()); + oldAccountByCommunicationProvider.setFirstName(request.getFirstName()); + oldAccountByCommunicationProvider.setAvatar(request.getAvatar().asReadOnlyByteBuffer()); + batchOps.update(oldAccountByCommunicationProvider); + } + + public boolean foundExistingNotOwnUsername(UUID accountId, String username) { + AccountByUsername foundAccountByUsername = accountByUsernameRepository.findByUsername(username); + if (foundAccountByUsername == null) { + return false; + } else if (!foundAccountByUsername.getAccountId().equals(accountId)) { + return true; + } else { + return false; + } + } + } diff --git a/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java b/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java index bb89aeed3090b9cab41f7c13db66c5ddd4280985..4d3c6f1eec8766ea9cef3ffbe029d9eb2970eb3e 100644 --- a/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java +++ b/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java @@ -14,6 +14,6 @@ import biz.nynja.account.models.PendingAccount; @Repository public interface PendingAccountRepository extends CassandraRepository { - public PendingAccount findByAccountId(UUID accountId); + PendingAccount findByAccountId(UUID accountId); } diff --git a/src/main/java/biz/nynja/account/repositories/ProfileRepository.java b/src/main/java/biz/nynja/account/repositories/ProfileRepository.java index 9f1dcf28ab7f9d398e0d126a2ea0f99a397c0828..94b350f27763923a796105280c0cacc6607cd693 100644 --- a/src/main/java/biz/nynja/account/repositories/ProfileRepository.java +++ b/src/main/java/biz/nynja/account/repositories/ProfileRepository.java @@ -13,6 +13,6 @@ import biz.nynja.account.models.Profile; @Repository public interface ProfileRepository extends CassandraRepository { - public Profile findByProfileId(UUID profileId); + Profile findByProfileId(UUID profileId); } diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index 8738c2c91890b2beec3ea8236e0841d8cb5df609..511cf5ddb01365dccfcc283df288a22a885864bb 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -33,10 +33,15 @@ import biz.nynja.account.models.AccountByAuthenticationProvider; import biz.nynja.account.models.AccountByProfileId; import biz.nynja.account.models.PendingAccount; import biz.nynja.account.repositories.AccountByAuthenticationProviderRepository; +import biz.nynja.account.models.Profile; import biz.nynja.account.repositories.AccountByProfileIdRepository; import biz.nynja.account.repositories.AccountRepository; import biz.nynja.account.repositories.AccountRepositoryAdditional; import biz.nynja.account.repositories.PendingAccountRepository; +import biz.nynja.account.grpc.AccountsResponse; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; +import biz.nynja.account.grpc.UpdateProfileResponse; import io.grpc.stub.StreamObserver; /** @@ -48,6 +53,8 @@ import io.grpc.stub.StreamObserver; public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBase { private static final Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class); + private static final byte MIN_NUMBER_OF_AUTH_PROVIDERS_IN_PROFILE = 1; + @Autowired private AccountRepository accountRepository; @@ -249,4 +256,67 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas return; } } + + @Override + public void updateProfile(UpdateProfileRequest request, StreamObserver responseObserver) { + + logger.info("Updating profile..."); + logger.debug("Updating profile...: {}", request); + + if (request.getAuthProvidersList().size() < MIN_NUMBER_OF_AUTH_PROVIDERS_IN_PROFILE) { + logger.info("Error updating profile. Check the number of authentication providers."); + logger.debug("Error updating profile. Check the number of authentication providers: {}", request); + responseObserver.onNext(UpdateProfileResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.ERROR_UPDATING_PROFILE)).build()); + responseObserver.onCompleted(); + return; + } + + Profile updatedProfile = accountRepositoryAdditional.updateProfile(request); + + if (updatedProfile == null) { + responseObserver.onNext(UpdateProfileResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.ERROR_UPDATING_PROFILE)).build()); + responseObserver.onCompleted(); + return; + } + logger.debug("Profile \"{}\" updated in the DB", updatedProfile.toString()); + logger.debug("Profile: \"{}\" updated successfully.", updatedProfile); + UpdateProfileResponse response = UpdateProfileResponse.newBuilder().setProfileResponse(updatedProfile.toProto()) + .build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + logger.info("Profile updated successfully."); + return; + } + + @Override + public void updateAccount(UpdateAccountRequest request, StreamObserver responseObserver) { + logger.info("Updating account..."); + logger.debug("Updating account...: {}", request); + + Cause validationCause = validator.validateUpdateAccountRequest(request); + if (validationCause != null) { + responseObserver.onNext(AccountResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(validationCause)).build()); + responseObserver.onCompleted(); + return; + } + + Account updatedAccount = accountRepositoryAdditional.updateAccount(request); + + if (updatedAccount == null) { + responseObserver.onNext(AccountResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.ERROR_UPDATING_ACCOUNT)).build()); + responseObserver.onCompleted(); + return; + } + logger.debug("Account \"{}\" updated in the DB", updatedAccount.toString()); + logger.debug("Account: \"{}\" updated successfully.", updatedAccount); + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(updatedAccount.toProto()).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + logger.info("Account updated successfully."); + return; + } } \ No newline at end of file